remotes/origin/PR-39
Mukhtar 9 years ago
commit 045d509a22
  1. 18
      api/views.py
  2. 2
      archilance/settings/base.py
  3. 38
      assets/index.js
  4. 2
      assets/js/chat.js
  5. 17
      chat/filters.py
  6. 16
      common/filters.py
  7. 28
      common/serializers.py
  8. 61
      projects/filters.py
  9. 21
      projects/forms.py
  10. 16
      projects/migrations/0031_merge.py
  11. 6
      projects/models.py
  12. 113
      projects/serializers.py
  13. 67
      projects/templates/project_detail.html
  14. 2
      projects/urls.py
  15. 6
      projects/views.py
  16. 10
      reviews/filters.py
  17. 7
      specializations/filters.py
  18. 2
      templates/partials/base.html
  19. 4
      users/filters.py
  20. 3
      users/templates/contractor_profile.html
  21. 3
      work_sell/filters.py
  22. 31
      work_sell/forms.py
  23. 16
      work_sell/migrations/0014_merge.py
  24. 10
      work_sell/models.py
  25. 65
      work_sell/templates/worksell_edit.html
  26. 26
      work_sell/views.py

@ -13,7 +13,7 @@ from projects.serializers import (
from projects.filters import ( from projects.filters import (
ProjectFilterSet, RealtyFilterSet, StageFilterSet, PortfolioFilterSet, OrderFilterSet, ProjectFilterSet, RealtyFilterSet, StageFilterSet, PortfolioFilterSet, OrderFilterSet,
PortfolioPhotoFilterSet, PortfolioPhotoFilterSet, AnswerFilterSet,
) )
from specializations.models import Specialization from specializations.models import Specialization
@ -22,7 +22,7 @@ from specializations.filters import SpecializationFilterSet
from users.models import User, ContractorResumeFiles, ContractorResume, Team from users.models import User, ContractorResumeFiles, ContractorResume, Team
from users.serializers import UserSerializer, ContractorResumeFilesSerializer, ContractorResumeSerializer, TeamSerializer from users.serializers import UserSerializer, ContractorResumeFilesSerializer, ContractorResumeSerializer, TeamSerializer
from users.filters import UserFilterSet, ContractorResumeFilesFilterSet, ContractorResumeFilterSet from users.filters import UserFilterSet, TeamFilterSet, ContractorResumeFilesFilterSet, ContractorResumeFilterSet
from common.models import Location from common.models import Location
from common.serializers import LocationSerializer from common.serializers import LocationSerializer
@ -93,6 +93,16 @@ class ProjectViewSet(ModelViewSet):
filter_class = ProjectFilterSet filter_class = ProjectFilterSet
# permission_classes = (permissions.IsAuthenticatedOrReadOnly,) # permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_queryset(self):
qs = super().get_queryset()
x_no_contractor_answer = self.request.GET.get('x_no_contractor_answer', '')
if x_no_contractor_answer.isdecimal():
qs = qs.exclude(answers__object_id=x_no_contractor_answer, answers__content_type__model='user')
return qs
class NoteViewSet(ModelViewSet): class NoteViewSet(ModelViewSet):
queryset = Notes.objects.all() queryset = Notes.objects.all()
@ -221,10 +231,10 @@ class WorkSellPhotoViewSet(ModelViewSet):
class AnswerViewSet(ModelViewSet): class AnswerViewSet(ModelViewSet):
queryset = Answer.objects.all() queryset = Answer.objects.all()
serializer_class = AnswerSerializer serializer_class = AnswerSerializer
# filter_class = AnswerFilterSet filter_class = AnswerFilterSet
class TeamViewSet(ModelViewSet): class TeamViewSet(ModelViewSet):
queryset = Team.objects.all() queryset = Team.objects.all()
serializer_class = TeamSerializer serializer_class = TeamSerializer
# filter_class = TeamFilterSet filter_class = TeamFilterSet

@ -266,7 +266,7 @@ if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend' EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
PAGE_SIZE = 10 # Non-api page size (regular views) PAGE_SIZE = 10 # Non-api page size (regular views)
API_PAGE_SIZE = 1000 # Django REST framework API_PAGE_SIZE = 100 # Django REST framework
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [ 'DEFAULT_AUTHENTICATION_CLASSES': [

@ -257,30 +257,42 @@ function initContractorSelect($select, excludeIds) {
// Order offer project select --------------------------------------------- // Order offer project select ---------------------------------------------
;(function() {
var projectSelectOptions = { var projectSelectOptions = {
language: 'ru', language: 'ru',
placeholder: 'Выберите проект', // Required by `allowClear` placeholder: 'Выберите проект', // Required by `allowClear`
allowClear: true, allowClear: true,
} }
function initProjectSelect($select, customerId) { function initProjectSelect($select, customerId, contractorId) {
return $.ajax({url: '/api/projects/?state=active&customer=' + customerId, method: 'GET', dataType: 'json'}) var urlObj = new URI('/api/projects/')
.then(function(res) {
urlObj.setQuery({
state: 'active',
customer: customerId,
order__contractor__isnull: true,
order__team__isnull: true,
x_no_contractor_answer: contractorId,
})
return $.get(urlObj.href()).then(function(res) {
var projects = res.results var projects = res.results
$select.select2(_.merge(projectSelectOptions, { $select.select2(_.merge(projectSelectOptions, {
data: _.map(function(project) { data: _.map(function(project) {
return { return {
id: project.id, id: project.id,
text: project.name, //text: project.name,
text: format('%s (%s)', project.name, project.id), // Tmp
origItem: project, origItem: project,
} }
}, projects), }, projects),
})) }))
}) })
} }
window.initProjectSelect = initProjectSelect
}())

@ -31,7 +31,7 @@ window.confirm = function (message, callback, caption) {
var SocketHandler = function () { var SocketHandler = function () {
domain = domain.replace(':' + port, ''); domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + '/chat/' + userId + '/'; var url = 'ws://' + domain + ':8888/chat/' + userId + '/';
var sock = new WebSocket(url); var sock = new WebSocket(url);
var intervalId; var intervalId;
sock.onopen = function () { sock.onopen = function () {

@ -5,6 +5,7 @@ from .models import Message, Notes, Documents
# #
# class DocumentsFilterSet(FilterSet): # class DocumentsFilterSet(FilterSet):
# file = AllLookupsFilter() # file = AllLookupsFilter()
# id = AllLookupsFilter()
# # sender = RelatedFilter('users.filters.UserFilterSet') # # sender = RelatedFilter('users.filters.UserFilterSet')
# # recipent = RelatedFilter('users.filters.UserFilterSet') # # recipent = RelatedFilter('users.filters.UserFilterSet')
# # order = RelatedFilter('projects.filters.OrderFilterSet') # # order = RelatedFilter('projects.filters.OrderFilterSet')
@ -15,13 +16,15 @@ from .models import Message, Notes, Documents
class MessageFilterSet(FilterSet): class MessageFilterSet(FilterSet):
text = AllLookupsFilter()
created = AllLookupsFilter() created = AllLookupsFilter()
sender = RelatedFilter('users.filters.UserFilterSet') id = AllLookupsFilter()
recipent = RelatedFilter('users.filters.UserFilterSet')
private_type = AllLookupsFilter() private_type = AllLookupsFilter()
team = RelatedFilter('users.filters.TeamFilterSet') text = AllLookupsFilter()
order = RelatedFilter('projects.filters.OrderFilterSet') order = RelatedFilter('projects.filters.OrderFilterSet')
recipent = RelatedFilter('users.filters.UserFilterSet')
sender = RelatedFilter('users.filters.UserFilterSet')
team = RelatedFilter('users.filters.TeamFilterSet')
class Meta: class Meta:
model = Message model = Message
@ -39,10 +42,12 @@ class DocumentFilterSet(FilterSet):
class NoteFilterSet(FilterSet): class NoteFilterSet(FilterSet):
text = AllLookupsFilter()
created = AllLookupsFilter() created = AllLookupsFilter()
sender = RelatedFilter('users.filters.UserFilterSet') id = AllLookupsFilter()
text = AllLookupsFilter()
recipent = RelatedFilter('users.filters.UserFilterSet') recipent = RelatedFilter('users.filters.UserFilterSet')
sender = RelatedFilter('users.filters.UserFilterSet')
class Meta: class Meta:
model = Notes model = Notes

@ -1,17 +1,29 @@
from django.contrib.contenttypes.models import ContentType
from rest_framework_filters import FilterSet, AllLookupsFilter, RelatedFilter from rest_framework_filters import FilterSet, AllLookupsFilter, RelatedFilter
from .models import Location from .models import Location
class LocationFilterSet(FilterSet): class LocationFilterSet(FilterSet):
children = RelatedFilter('common.filters.LocationFilterSet')
id = AllLookupsFilter() id = AllLookupsFilter()
level = AllLookupsFilter() level = AllLookupsFilter()
lft = AllLookupsFilter() lft = AllLookupsFilter()
name = AllLookupsFilter() name = AllLookupsFilter()
parent = RelatedFilter('common.filters.LocationFilterSet')
rght = AllLookupsFilter() rght = AllLookupsFilter()
tree_id = AllLookupsFilter() tree_id = AllLookupsFilter()
type = AllLookupsFilter() type = AllLookupsFilter()
children = RelatedFilter('common.filters.LocationFilterSet')
parent = RelatedFilter('common.filters.LocationFilterSet')
class Meta: class Meta:
model = Location model = Location
class ContentTypeFilterSet(FilterSet):
app_label = AllLookupsFilter()
id = AllLookupsFilter()
model = AllLookupsFilter()
class Meta:
model = ContentType

@ -1,3 +1,4 @@
from django.contrib.contenttypes.models import ContentType
from rest_framework.serializers import ModelSerializer from rest_framework.serializers import ModelSerializer
from .models import Location from .models import Location
@ -8,15 +9,15 @@ class NestedLocationSerializer(ModelSerializer):
model = Location model = Location
fields = ( fields = (
'children',
'id', 'id',
'level',
'lft',
'name', 'name',
'children',
'parent', 'parent',
'type',
'lft',
'rght', 'rght',
'level',
'tree_id', 'tree_id',
'type',
) )
@ -28,13 +29,24 @@ class LocationSerializer(ModelSerializer):
model = Location model = Location
fields = ( fields = (
'children',
'id', 'id',
'level',
'lft',
'name', 'name',
'children',
'parent', 'parent',
'type',
'lft',
'rght', 'rght',
'level',
'tree_id', 'tree_id',
'type',
)
class ContentTypeSerializer(ModelSerializer):
class Meta:
model = ContentType
fields = (
'app_label',
'id',
'model',
) )

@ -1,6 +1,16 @@
from rest_framework_filters import FilterSet, RelatedFilter, AllLookupsFilter from rest_framework_filters import FilterSet, RelatedFilter, AllLookupsFilter
from .models import Project, Stage, Order, Realty, BuildingClassfication, ConstructionType, Portfolio, PortfolioPhoto from .models import (
Answer,
BuildingClassfication,
ConstructionType,
Order,
Portfolio,
PortfolioPhoto,
Project,
Realty,
Stage,
)
class BuildingClassficationFilterSet(FilterSet): class BuildingClassficationFilterSet(FilterSet):
@ -26,6 +36,7 @@ class ProjectFilterSet(FilterSet):
cro = AllLookupsFilter() cro = AllLookupsFilter()
currency = AllLookupsFilter() currency = AllLookupsFilter()
deal_type = AllLookupsFilter() deal_type = AllLookupsFilter()
id = AllLookupsFilter()
name = AllLookupsFilter() name = AllLookupsFilter()
price_and_term_required = AllLookupsFilter() price_and_term_required = AllLookupsFilter()
state = AllLookupsFilter() state = AllLookupsFilter()
@ -34,7 +45,9 @@ class ProjectFilterSet(FilterSet):
text = AllLookupsFilter() text = AllLookupsFilter()
work_type = AllLookupsFilter() work_type = AllLookupsFilter()
answers = RelatedFilter('projects.filters.AnswerFilterSet')
customer = RelatedFilter('users.filters.UserFilterSet') customer = RelatedFilter('users.filters.UserFilterSet')
order = RelatedFilter('projects.filters.OrderFilterSet')
realty = RelatedFilter('projects.filters.RealtyFilterSet') realty = RelatedFilter('projects.filters.RealtyFilterSet')
specialization = RelatedFilter('specializations.filters.SpecializationFilterSet') specialization = RelatedFilter('specializations.filters.SpecializationFilterSet')
@ -42,31 +55,58 @@ class ProjectFilterSet(FilterSet):
model = Project model = Project
class OrderFilterSet(FilterSet): class AnswerFilterSet(FilterSet):
id = AllLookupsFilter() budget = AllLookupsFilter()
contractor = RelatedFilter('users.filters.UserFilterSet')
team = RelatedFilter('users.filters.TeamFilterSet')
created = AllLookupsFilter() created = AllLookupsFilter()
currency = AllLookupsFilter()
id = AllLookupsFilter()
is_archive = AllLookupsFilter()
object_id = AllLookupsFilter()
rejected = AllLookupsFilter()
secure_deal_only = AllLookupsFilter()
term = AllLookupsFilter()
term_type = AllLookupsFilter()
# author = ... # ???
# messages = RelatedFilter('...')
content_type = RelatedFilter('common.filters.ContentTypeFilterSet')
contractors = RelatedFilter('users.filters.UserFilterSet')
portfolios = RelatedFilter('projects.filters.PortfolioFilterSet')
project = RelatedFilter('projects.filters.ProjectFilterSet') project = RelatedFilter('projects.filters.ProjectFilterSet')
teams = RelatedFilter('users.filters.TeamFilterSet')
class Meta:
model = Answer
class OrderFilterSet(FilterSet):
created = AllLookupsFilter()
id = AllLookupsFilter()
secure = AllLookupsFilter() secure = AllLookupsFilter()
status = AllLookupsFilter() status = AllLookupsFilter()
contractor = RelatedFilter('users.filters.UserFilterSet')
project = RelatedFilter('projects.filters.ProjectFilterSet')
team = RelatedFilter('users.filters.TeamFilterSet')
class Meta: class Meta:
model = Order model = Order
class StageFilterSet(FilterSet): class StageFilterSet(FilterSet):
cost = AllLookupsFilter()
cost_type = AllLookupsFilter()
id = AllLookupsFilter() id = AllLookupsFilter()
is_paid = AllLookupsFilter()
name = AllLookupsFilter() name = AllLookupsFilter()
pos = AllLookupsFilter()
result = AllLookupsFilter() result = AllLookupsFilter()
cost = AllLookupsFilter() status = AllLookupsFilter()
term = AllLookupsFilter() term = AllLookupsFilter()
cost_type = AllLookupsFilter()
term_type = AllLookupsFilter() term_type = AllLookupsFilter()
status = AllLookupsFilter()
pos = AllLookupsFilter()
order = RelatedFilter('projects.filters.OrderFilterSet') order = RelatedFilter('projects.filters.OrderFilterSet')
is_paid = AllLookupsFilter()
class Meta: class Meta:
model = Stage model = Stage
@ -87,6 +127,7 @@ class RealtyFilterSet(FilterSet):
class PortfolioPhotoFilterSet(FilterSet): class PortfolioPhotoFilterSet(FilterSet):
id = AllLookupsFilter() id = AllLookupsFilter()
portfolio = RelatedFilter('projects.filters.PortfolioFilterSet') portfolio = RelatedFilter('projects.filters.PortfolioFilterSet')
# img = ??? # img = ???

@ -207,20 +207,23 @@ class PortfolioEditForm(forms.ModelForm):
fields = ( fields = (
'building_classification', 'building_classification',
'construction_type', 'construction_type',
'specialization',
'budget',
'currency', 'currency',
'description',
'name', 'name',
'photos', 'term',
'term_type', 'term_type',
'worksell',
) )
# fields = '__all__' widgets = {
# 'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
# widgets = { 'building_classification': forms.Select(attrs={'class': 'selectpicker'}),
# 'construction_type': forms.Select(attrs={'class': 'selectpicker'}), 'currency': forms.Select(attrs={'class': 'selectpicker'}),
# 'building_classification': forms.Select(attrs={'class': 'selectpicker'}), 'term_type': forms.Select(attrs={'class': 'selectpicker'}),
# 'currency': forms.Select(attrs={'class': 'selectpicker'}), }
# 'term_type': forms.Select(attrs={'class': 'selectpicker'}),
# }
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# self.request = kwargs.pop('request') # self.request = kwargs.pop('request')

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-14 13:00
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('projects', '0030_auto_20160912_1912'),
('projects', '0030_auto_20160912_1305'),
]
operations = [
]

@ -119,7 +119,7 @@ class Project(models.Model, HitCountMixin):
class ProjectFile(models.Model): class ProjectFile(models.Model):
file = models.FileField(upload_to='projects/project_files') file = models.FileField(upload_to='projects/project_files/')
project = models.ForeignKey(Project, related_name='files', blank=True, null=True) project = models.ForeignKey(Project, related_name='files', blank=True, null=True)
class Meta: class Meta:
@ -189,7 +189,7 @@ class AnswerMessage(models.Model):
class AnswerFile(models.Model): class AnswerFile(models.Model):
answer = models.ForeignKey(Answer, related_name='files', blank=True, null=True) answer = models.ForeignKey(Answer, related_name='files', blank=True, null=True)
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
file = models.FileField(upload_to='projects/answer_files') file = models.FileField(upload_to='projects/answer_files/')
class Meta: class Meta:
verbose_name = 'Файл для отклика' verbose_name = 'Файл для отклика'
@ -344,7 +344,7 @@ class Portfolio(models.Model):
class PortfolioPhoto(models.Model): class PortfolioPhoto(models.Model):
img = models.ImageField(upload_to='projects/portfolio') img = models.ImageField(upload_to='projects/portfolio/')
portfolio = models.ForeignKey(Portfolio, related_name='photos') portfolio = models.ForeignKey(Portfolio, related_name='photos')
class Meta: class Meta:

@ -3,11 +3,40 @@ from generic_relations.relations import GenericRelatedField
from rest_framework.serializers import ModelSerializer, ImageField, FileField, SerializerMethodField, PrimaryKeyRelatedField, ReadOnlyField from rest_framework.serializers import ModelSerializer, ImageField, FileField, SerializerMethodField, PrimaryKeyRelatedField, ReadOnlyField
from .models import Project, Realty, BuildingClassfication, ConstructionType, Order, Stage, Portfolio, PortfolioPhoto, Answer, AnswerFile from .models import Project, Realty, BuildingClassfication, ConstructionType, Order, Stage, Portfolio, PortfolioPhoto, Answer, AnswerFile
from common.serializers import LocationSerializer from common.serializers import LocationSerializer, ContentTypeSerializer
from specializations.serializers import SpecializationSerializer from specializations.serializers import SpecializationSerializer
from users.models import User, Team from users.models import User, Team
from users.serializers import UserSerializer, TeamSerializer from users.serializers import UserSerializer, TeamSerializer
class AnswerFileSerializer(ModelSerializer):
file = FileField()
class Meta:
model = AnswerFile
fields = (
'file',
'id',
'name',
# 'answer',
)
class PortfolioPhotoSerializer(ModelSerializer):
img = ImageField()
portfolio_id = PrimaryKeyRelatedField(read_only=True, source='portfolio')
class Meta:
model = PortfolioPhoto
fields = (
'id',
'img',
'portfolio_id',
)
class BuildingClassficationSerializer(ModelSerializer): class BuildingClassficationSerializer(ModelSerializer):
class Meta: class Meta:
model = BuildingClassfication model = BuildingClassfication
@ -93,16 +122,53 @@ class OrderSerializer_(ModelSerializer):
) )
class AnswerSerializer_(ModelSerializer):
project_id = PrimaryKeyRelatedField(read_only=True, source='project')
portfolio_ids = PrimaryKeyRelatedField(read_only=True, source='portfolios', many=True)
content_type = ContentTypeSerializer()
files = AnswerFileSerializer(many=True)
author = GenericRelatedField({
User: UserSerializer(),
Team: TeamSerializer()
})
class Meta:
model = Answer
fields = (
'budget',
'created',
'currency',
'id',
'is_archive',
'object_id',
'portfolio_ids',
'project_id',
'rejected',
'secure_deal_only',
'term',
'term_type',
'author', # Generic related field
'content_type',
'files',
)
class ProjectSerializer(ModelSerializer): class ProjectSerializer(ModelSerializer):
answers = AnswerSerializer_(many=True)
customer = UserSerializer() customer = UserSerializer()
specialization = SpecializationSerializer()
realty = RealtySerializer()
order = OrderSerializer_() # TODO: Can't serialize a reverse/reciprocal relation order = OrderSerializer_() # TODO: Can't serialize a reverse/reciprocal relation
realty = RealtySerializer()
specialization = SpecializationSerializer()
class Meta: class Meta:
model = Project model = Project
fields = ( fields = (
'answers',
'budget', 'budget',
'budget_by_agreement', 'budget_by_agreement',
'created', 'created',
@ -158,25 +224,10 @@ class OrderSerializer(ModelSerializer):
return False return False
class PortfolioPhotoSerializer(ModelSerializer):
img = ImageField()
portfolio_id = PrimaryKeyRelatedField(read_only=True, source='portfolio')
class Meta:
model = PortfolioPhoto
fields = (
'id',
'img',
'portfolio_id',
)
class PortfolioSerializer(ModelSerializer): class PortfolioSerializer(ModelSerializer):
# answers = AnswerSerializer(many=True) # answers = AnswerSerializer(many=True)
building_classification = BuildingClassficationSerializer() building_classification = BuildingClassficationSerializer()
construction_type = ConstructionTypeSerializer() construction_type = ConstructionTypeSerializer()
id = ReadOnlyField()
location = LocationSerializer() location = LocationSerializer()
photos = PortfolioPhotoSerializer(many=True) photos = PortfolioPhotoSerializer(many=True)
specialization = SpecializationSerializer() specialization = SpecializationSerializer()
@ -204,24 +255,11 @@ class PortfolioSerializer(ModelSerializer):
) )
class AnswerFileSerializer(ModelSerializer):
file = FileField()
class Meta:
model = AnswerFile
fields = (
'file',
'id',
'name',
# 'answer',
)
class AnswerSerializer(ModelSerializer): class AnswerSerializer(ModelSerializer):
content_type = ContentTypeSerializer()
files = AnswerFileSerializer(many=True) files = AnswerFileSerializer(many=True)
portfolios = PortfolioSerializer(many=True) portfolios = PortfolioSerializer(many=True)
project = ProjectSerializer project = ProjectSerializer()
author = GenericRelatedField({ author = GenericRelatedField({
User: UserSerializer(), User: UserSerializer(),
@ -236,15 +274,18 @@ class AnswerSerializer(ModelSerializer):
'created', 'created',
'currency', 'currency',
'id', 'id',
'is_archive',
'object_id',
'rejected',
'secure_deal_only', 'secure_deal_only',
'term', 'term',
'term_type', 'term_type',
'text',
'author', # Generic related field
'content_type',
'files', 'files',
'portfolios', 'portfolios',
'project', 'project',
'author', # Generic related field
# 'candidates', # 'candidates',
) )

@ -109,7 +109,7 @@
<div class="col-lg-10 col-lg-offset-1"> <div class="col-lg-10 col-lg-offset-1">
{% if request.user.is_contractor and not answer %} {% if request.user.is_contractor and not answer %}
<a href="#" onclick="$('.-project-answer-form').toggle('slow'); return false" class="new-answer"> <a href="#" onclick="toggleProjectAnswerForm(); return false" class="new-answer">
Ответить на проект Ответить на проект
</a> </a>
@ -283,6 +283,18 @@
<input type="hidden" name="next" value="{{ request.path }}"> <input type="hidden" name="next" value="{{ request.path }}">
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink3" title="{{ answer.pk }}">отказ</a> <a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink3" title="{{ answer.pk }}">отказ</a>
</form> </form>
{% if project.order.contractor and project.order.contractor == answer.author %}
<a href="/chat/#order{{ project.order.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% elif project.order.team and project.order.team == answer.author %}
<a href="/chat/#order{{ project.order.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% else %}
{% if answer.author|class_name == 'User' %}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% elif answer.author|class_name == 'Team'%}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.owner.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% endif %}
{% endif %}
</div> </div>
@ -361,7 +373,7 @@
</div> </div>
</div> </div>
{% else %} {% else %}
<form action="{% url 'projects:detail' pk=project.pk %}" method="POST" enctype="multipart/form-data" novalidate class="proj_answ_form -project-answer-form" s--tyle="display: none"> <form action="{% url 'projects:detail' pk=project.pk %}" method="POST" enctype="multipart/form-data" novalidate class="proj_answ_form -project-answer-form" style="display: none">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="next" value="{% url 'projects:detail' pk=project.pk %}"> <input type="hidden" name="next" value="{% url 'projects:detail' pk=project.pk %}">
<input type="hidden" name="answer_as_team" value="{% if answer_as_team %}on{% endif %}"> <input type="hidden" name="answer_as_team" value="{% if answer_as_team %}on{% endif %}">
@ -655,6 +667,12 @@
предложить проект предложить проект
</a> </a>
</form> </form>
{% else %}
{% if answer.author|class_name == 'User' %}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% elif answer.author|class_name == 'Team'%}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.owner.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% endif %}
{% endif %} {% endif %}
<form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate> <form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate>
@ -843,6 +861,12 @@
предложить проект предложить проект
</a> </a>
</form> </form>
{% else %}
{% if answer.author|class_name == 'User' %}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% elif answer.author|class_name == 'Team'%}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.owner.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% endif %}
{% endif %} {% endif %}
<form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate> <form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate>
@ -1016,6 +1040,12 @@
<input type="hidden" name="next" value="{{ request.path }}"> <input type="hidden" name="next" value="{{ request.path }}">
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink1" title="{{ answer.pk }}">Восстановить</a> <a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink1" title="{{ answer.pk }}">Восстановить</a>
</form> </form>
{% if answer.author|class_name == 'User' %}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% elif answer.author|class_name == 'Team'%}
<a href="{% url 'chat:chat-user' %}?user_id={{ answer.author.owner.pk }}" class="candLink candLink2">Перейти к обсуждению</a>
{% endif %}
</div> </div>
<div class="col-xs-12"> <div class="col-xs-12">
@ -1083,9 +1113,10 @@
{% block js_block %} {% block js_block %}
<script> <script>
(function() { // Persistent Bootstrap tabs --------------------------------------
// Persistent Bootstrap tabs --------------------------------------
;(function() {
var $allTabs = $('a[data-toggle="tab"]') var $allTabs = $('a[data-toggle="tab"]')
var $tab = $allTabs.filter('[href="' + window.location.hash + '"]').first() var $tab = $allTabs.filter('[href="' + window.location.hash + '"]').first()
@ -1104,5 +1135,31 @@
window.location.hash = $tab.attr('href') window.location.hash = $tab.attr('href')
}) })
}()) }())
// Toggle project answer form -----------------------------------
;(function() {
var $projectAnswerForm = $('.-project-answer-form').first()
if ($.cookie('projectAnswerFormVisible')) {
$projectAnswerForm.show('slow')
$.cookie('projectAnswerFormVisible', 'true', {expires: new Date(new Date().getTime() + 300000)})
}
function toggleProjectAnswerForm() {
if ($.cookie('projectAnswerFormVisible')) {
$projectAnswerForm.hide('slow')
$.cookie('projectAnswerFormVisible', '', {expires: new Date(new Date().getTime() + 300000)})
} else {
$projectAnswerForm.show('slow')
$.cookie('projectAnswerFormVisible', 'true', {expires: new Date(new Date().getTime() + 300000)})
}
}
window.toggleProjectAnswerForm = toggleProjectAnswerForm
}())
</script> </script>
{% endblock %} {% endblock %}

@ -47,7 +47,7 @@ urlpatterns = [
urls.url(r'^answer/move/archive/$', ContractorAnswerArchiveView.as_view(), name='contractor-answer-archive'), urls.url(r'^answer/move/archive/$', ContractorAnswerArchiveView.as_view(), name='contractor-answer-archive'),
urls.url(r'^portfolio/create/$', contractor_portfolio_create, name='contractor-portfolio-create'), 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+)/$', 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'^portfolio/(?P<pk>\d+)/edit/$', ContractorPortfolioUpdateView.as_view(), name='contractor-portfolio-edit'),
urls.url(r'^portfolio/(?P<pk>\d+)/trash/$', ContractorPortfolioTrashView.as_view(), name='contractor-portfolio-trash'), urls.url(r'^portfolio/(?P<pk>\d+)/trash/$', ContractorPortfolioTrashView.as_view(), name='contractor-portfolio-trash'),

@ -762,7 +762,7 @@ class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
template_name = 'contractor_portfolio_edit.html' template_name = 'contractor_portfolio_edit.html'
def get_success_url(self): def get_success_url(self):
return reverse('users:contractor-profile',kwargs={'pk':self.object.user_id}) return reverse('projects:contractor-portfolio-detail', kwargs={'pk': self.object.pk})
def form_valid(self, form): def form_valid(self, form):
portfolio = form.instance portfolio = form.instance
@ -781,11 +781,9 @@ class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
live_images = form.cleaned_data['live_images'] live_images = form.cleaned_data['live_images']
# import code; code.interact(local=dict(globals(), **locals()))
for live_image in live_images: for live_image in live_images:
new_image = ContentFile(live_image.file.read()) new_image = ContentFile(live_image.file.read())
# new_image.name = live_image.file.name new_image.name = live_image.file.name
PortfolioPhoto.objects.create(img=new_image, portfolio=portfolio) PortfolioPhoto.objects.create(img=new_image, portfolio=portfolio)

@ -4,15 +4,15 @@ from .models import Review
class ReviewFilterSet(FilterSet): class ReviewFilterSet(FilterSet):
created = AllLookupsFilter()
id = AllLookupsFilter()
text = AllLookupsFilter() text = AllLookupsFilter()
type = AllLookupsFilter() type = AllLookupsFilter()
created = AllLookupsFilter()
from_customer = RelatedFilter('users.filters.UserFilterSet')
from_contractor = RelatedFilter('users.filters.UserFilterSet') from_contractor = RelatedFilter('users.filters.UserFilterSet')
target_customer = RelatedFilter('users.filters.UserFilterSet') from_customer = RelatedFilter('users.filters.UserFilterSet')
target_contractor = RelatedFilter('users.filters.UserFilterSet') target_contractor = RelatedFilter('users.filters.UserFilterSet')
target_customer = RelatedFilter('users.filters.UserFilterSet')
class Meta: class Meta:
model = Review model = Review

@ -2,15 +2,16 @@ from rest_framework_filters import FilterSet, RelatedFilter, AllLookupsFilter
from .models import Specialization from .models import Specialization
class SpecializationFilterSet(FilterSet): class SpecializationFilterSet(FilterSet):
children = RelatedFilter('specializations.filters.SpecializationFilterSet')
id = AllLookupsFilter() id = AllLookupsFilter()
level = AllLookupsFilter() level = AllLookupsFilter()
lft = AllLookupsFilter() lft = AllLookupsFilter()
name = AllLookupsFilter() name = AllLookupsFilter()
parent = RelatedFilter('specializations.filters.SpecializationFilterSet')
projects = RelatedFilter('projects.filters.ProjectFilterSet')
rght = AllLookupsFilter() rght = AllLookupsFilter()
tree_id = AllLookupsFilter() tree_id = AllLookupsFilter()
children = RelatedFilter('specializations.filters.SpecializationFilterSet')
parent = RelatedFilter('specializations.filters.SpecializationFilterSet')
projects = RelatedFilter('projects.filters.ProjectFilterSet')
class Meta: class Meta:
model = Specialization model = Specialization

@ -86,7 +86,7 @@
if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) { if ((queryString.indexOf('/chat') != 0) && (queryString.indexOf('/users/contractor-office/510/work-projects') != 0)) {
domain = domain.replace(':' + port, ''); domain = domain.replace(':' + port, '');
var url = 'ws://' + domain + '/chat/' + userId + '/'; var url = 'ws://' + domain + ':8888/chat/' + userId + '/';
var sock = new WebSocket(url); var sock = new WebSocket(url);
var intervalId; var intervalId;
sock.onopen = function () { sock.onopen = function () {

@ -47,8 +47,8 @@ class TeamFilterSet(FilterSet):
class ContractorResumeFilterSet(FilterSet): class ContractorResumeFilterSet(FilterSet):
id = AllLookupsFilter() id = AllLookupsFilter()
text = AllLookupsFilter()
resume_file = AllLookupsFilter() resume_file = AllLookupsFilter()
text = AllLookupsFilter()
class Meta: class Meta:
model = ContractorResume model = ContractorResume
@ -56,8 +56,8 @@ class ContractorResumeFilterSet(FilterSet):
class ContractorResumeFilesFilterSet(FilterSet): class ContractorResumeFilesFilterSet(FilterSet):
id = AllLookupsFilter() id = AllLookupsFilter()
title = AllLookupsFilter()
img = AllLookupsFilter() img = AllLookupsFilter()
title = AllLookupsFilter()
resume = RelatedFilter('users.filters.ContractorResumeFilterSet') resume = RelatedFilter('users.filters.ContractorResumeFilterSet')

@ -738,6 +738,7 @@
{% if request.user.is_customer %} {% if request.user.is_customer %}
var customerId = {{ request.user.pk }} var customerId = {{ request.user.pk }}
var contractorId = {{ contractor.pk }}
var $projectSelectionModal = $('#projectSelectionModal') var $projectSelectionModal = $('#projectSelectionModal')
var projectUrl_ = '/projects/%id%/' var projectUrl_ = '/projects/%id%/'
var projectOrderChatUrl_ = '/chat/#order%orderId%' var projectOrderChatUrl_ = '/chat/#order%orderId%'
@ -751,7 +752,7 @@
$that.data('modalOpenButton', $modalOpenButton) $that.data('modalOpenButton', $modalOpenButton)
initProjectSelect($projectSelect, customerId) initProjectSelect($projectSelect, customerId, contractorId)
}) })

@ -15,9 +15,10 @@ class WorkSellFilter(django_filters.FilterSet):
class WorkSellPhotoFilterSet(FilterSet): class WorkSellPhotoFilterSet(FilterSet):
id = AllLookupsFilter() id = AllLookupsFilter()
work_sell = RelatedFilter('work_sell.filters.WorkSellFilterSet')
# img = ??? # img = ???
work_sell = RelatedFilter('work_sell.filters.WorkSellFilterSet')
class Meta: class Meta:
model = WorkSellPhoto model = WorkSellPhoto

@ -1,8 +1,8 @@
import itertools import itertools
from django import forms from django import forms
from common.models import Location from common.models import Location, LiveImageUpload
from .models import WorkSell from .models import WorkSell, WorkSellPhoto
from specializations.models import Specialization from specializations.models import Specialization
@ -17,19 +17,32 @@ class ContractorWorkSellTrashForm(forms.Form):
class WorkSellForm(forms.ModelForm): class WorkSellForm(forms.ModelForm):
# Define a form field manually for a reverse model vield:
photos = forms.ModelMultipleChoiceField(
queryset=WorkSellPhoto.objects.none(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
live_images = forms.ModelMultipleChoiceField(
queryset=LiveImageUpload.objects.all(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
class Meta: class Meta:
model = WorkSell model = WorkSell
fields = ( fields = (
'budget',
'building_classification', 'building_classification',
'construction_type', 'construction_type',
'contractor', 'specialization',
'budget',
'currency', 'currency',
'description', 'description',
'location',
'name', 'name',
'specialization',
'term', 'term',
'term_type', 'term_type',
) )
@ -43,10 +56,8 @@ class WorkSellForm(forms.ModelForm):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# self.request = kwargs.pop('request') # self.request = kwargs.pop('request')
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.fields['specialization'].queryset = Specialization.objects.root_nodes()[0].get_descendants() self.fields['photos'].queryset = self.instance.photos.all()
self.fields['location'].queryset = Location.objects.root_nodes()[0].get_descendants()
# self.fields['location'].queryset = Location.objects # Migrate with this enabled
class WorkSellFilterForm(forms.ModelForm): class WorkSellFilterForm(forms.ModelForm):

@ -0,0 +1,16 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-09-14 13:00
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('work_sell', '0013_auto_20160912_1305'),
('work_sell', '0013_auto_20160912_1912'),
]
operations = [
]

@ -54,19 +54,19 @@ class WorkSell(models.Model):
class WorkSellPhoto(models.Model): class WorkSellPhoto(models.Model):
img = ImageField(upload_to='worksell/worksell') img = ImageField(upload_to='worksell/worksell/')
worksell = models.ForeignKey(WorkSell, related_name='photos') worksell = models.ForeignKey(WorkSell, related_name='photos')
def __str__(self):
return self.worksell.name
class Meta: class Meta:
verbose_name = 'Изображение Готовая работа' verbose_name = 'Изображение Готовая работа'
verbose_name_plural = 'Изображения Готовые работы' verbose_name_plural = 'Изображения Готовые работы'
def __str__(self):
return self.img and self.img.url or str(self.img)
class Picture(models.Model): class Picture(models.Model):
file = models.ImageField(upload_to='worksell/pictures') file = models.ImageField(upload_to='worksell/pictures/')
slug = models.SlugField(max_length=50, blank=True) slug = models.SlugField(max_length=50, blank=True)
def __str__(self): def __str__(self):

@ -1,6 +1,21 @@
{% extends 'partials/base.html' %} {% extends 'partials/base.html' %}
{% load common_tags %} {% load thumbnail %}
{% block head_css %}
<style>
.-live-image-upload-container .-position-relative-parent {position: relative}
.-live-image-upload-container .-image-delete,
.-live-image-upload-container .-live-image-delete {
position: absolute;
top: 0;
right: 0;
background-color: white;
}
</style>
{% endblock %}
{% block content %} {% block content %}
{% include 'partials/header.html' %} {% include 'partials/header.html' %}
@ -26,7 +41,8 @@
<div class="textAreaBlock2 text-nn box-sizing disTab"> <div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Специализации</p> <p>Специализации</p>
</div> </div>
<div class="-spec-select-container">
<div class="col-lg-3"> <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>
@ -43,9 +59,9 @@
<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> </div>
<input type="hidden" id="chosenSpecId" name="{{ form.specialization.html_name }}" <input type="hidden" class="-chosen-spec-id" name="{{ form.specialization.html_name }}"
value="{{ form.specialization.value }}"> value="{{ form.specialization.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab"> <div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ form.budget.errors.as_text }}</p> <p>Бюджет{{ form.budget.errors.as_text }}</p>
@ -82,6 +98,47 @@
<p>Классификация здания</p> <p>Классификация здания</p>
{{ form.building_classification}} {{ form.building_classification}}
</div> </div>
<div class="textAreaBlock2 text-nn box-sizing disTab -live-image-upload-container">
<p>Фотографии</p>
{% for photo in form.photos.field.queryset.all %}
<div class="col-lg-3 -image-widget">
<div class="-position-relative-parent" style="display: inline-block">
<a href="#" onclick="return false" class="btn close -image-delete">&times;</a>
{% thumbnail photo.img "200x200" crop="center" as img %}
<img src="{{ img.url }}">
{% endthumbnail %}
</div>
<input type="checkbox" name="{{ form.photos.html_name }}" value="{{ photo.pk }}" checked style='display: none'>
</div>
{% endfor %}
<script type="text/x-template" class="-templ">
<% images.forEach(function(image) { %>
<div class="col-lg-3">
<div class="-position-relative-parent" style="display: inline-block">
<a href="#" onclick="return false" data-image-id="<%- image.id %>" class="btn close -live-image-delete">&times;</a>
<img src="<%- image.smallThumbnailUrl %>">
</div>
<input type="checkbox" name="{{ form.live_images.html_name }}" value="<%- image.id %>" checked style='display: none'>
</div>
<% }) %>
</script>
<div class="-res"></div>
<div class="col-xs-12">
<input type="file" name="image" multiple class="-live-image-upload-field" style="display: none">
<a href="#" onclick="$(this).closest('.-live-image-upload-container').find('.-live-image-upload-field').first().click(); return false" class="btn btn-default add_file_to_port">
Выберите файлы
</a>
</div>
</div>
<div class="polsF1 polsF2 disTab"> <div class="polsF1 polsF2 disTab">

@ -178,13 +178,35 @@ class WorkSellUpdateView(UpdateView):
template_name = 'worksell_edit.html' template_name = 'worksell_edit.html'
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
# import code; code.interact(local=dict(globals(), **locals()))
if self.get_object().contractor_id != request.user.pk: if self.get_object().contractor_id != request.user.pk:
return HttpResponseForbidden('403 Forbidden') return HttpResponseForbidden('403 Forbidden')
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_success_url(self): def get_success_url(self):
return reverse('work_sell:list') return reverse('work_sell:detail', kwargs={'pk': self.object.pk})
def form_valid(self, form):
worksell = form.instance
photos = form.cleaned_data['photos']
WorkSellPhoto.objects.filter(worksell=worksell).delete()
for photo in photos:
WorkSellPhoto.objects.create(img=photo.img, worksell=worksell)
live_images = form.cleaned_data['live_images']
for live_image in live_images:
new_image = ContentFile(live_image.file.read())
new_image.name = live_image.file.name
WorkSellPhoto.objects.create(img=new_image, worksell=worksell)
live_image.file.delete()
live_image.delete()
return super().form_valid(form)
class WorkSellDeleteView(DeleteView): class WorkSellDeleteView(DeleteView):

Loading…
Cancel
Save