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 (
ProjectFilterSet, RealtyFilterSet, StageFilterSet, PortfolioFilterSet, OrderFilterSet,
PortfolioPhotoFilterSet,
PortfolioPhotoFilterSet, AnswerFilterSet,
)
from specializations.models import Specialization
@ -22,7 +22,7 @@ from specializations.filters import SpecializationFilterSet
from users.models import User, ContractorResumeFiles, ContractorResume, Team
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.serializers import LocationSerializer
@ -93,6 +93,16 @@ class ProjectViewSet(ModelViewSet):
filter_class = ProjectFilterSet
# 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):
queryset = Notes.objects.all()
@ -221,10 +231,10 @@ class WorkSellPhotoViewSet(ModelViewSet):
class AnswerViewSet(ModelViewSet):
queryset = Answer.objects.all()
serializer_class = AnswerSerializer
# filter_class = AnswerFilterSet
filter_class = AnswerFilterSet
class TeamViewSet(ModelViewSet):
queryset = Team.objects.all()
serializer_class = TeamSerializer
# filter_class = TeamFilterSet
filter_class = TeamFilterSet

@ -266,7 +266,7 @@ if DEBUG:
EMAIL_BACKEND = 'django.core.mail.backends.dummy.EmailBackend'
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 = {
'DEFAULT_AUTHENTICATION_CLASSES': [

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

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

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

@ -1,17 +1,29 @@
from django.contrib.contenttypes.models import ContentType
from rest_framework_filters import FilterSet, AllLookupsFilter, RelatedFilter
from .models import Location
class LocationFilterSet(FilterSet):
children = RelatedFilter('common.filters.LocationFilterSet')
id = AllLookupsFilter()
level = AllLookupsFilter()
lft = AllLookupsFilter()
name = AllLookupsFilter()
parent = RelatedFilter('common.filters.LocationFilterSet')
rght = AllLookupsFilter()
tree_id = AllLookupsFilter()
type = AllLookupsFilter()
children = RelatedFilter('common.filters.LocationFilterSet')
parent = RelatedFilter('common.filters.LocationFilterSet')
class Meta:
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 .models import Location
@ -8,15 +9,15 @@ class NestedLocationSerializer(ModelSerializer):
model = Location
fields = (
'children',
'id',
'level',
'lft',
'name',
'children',
'parent',
'type',
'lft',
'rght',
'level',
'tree_id',
'type',
)
@ -28,13 +29,24 @@ class LocationSerializer(ModelSerializer):
model = Location
fields = (
'children',
'id',
'level',
'lft',
'name',
'children',
'parent',
'type',
'lft',
'rght',
'level',
'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 .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):
@ -26,6 +36,7 @@ class ProjectFilterSet(FilterSet):
cro = AllLookupsFilter()
currency = AllLookupsFilter()
deal_type = AllLookupsFilter()
id = AllLookupsFilter()
name = AllLookupsFilter()
price_and_term_required = AllLookupsFilter()
state = AllLookupsFilter()
@ -34,7 +45,9 @@ class ProjectFilterSet(FilterSet):
text = AllLookupsFilter()
work_type = AllLookupsFilter()
answers = RelatedFilter('projects.filters.AnswerFilterSet')
customer = RelatedFilter('users.filters.UserFilterSet')
order = RelatedFilter('projects.filters.OrderFilterSet')
realty = RelatedFilter('projects.filters.RealtyFilterSet')
specialization = RelatedFilter('specializations.filters.SpecializationFilterSet')
@ -42,31 +55,58 @@ class ProjectFilterSet(FilterSet):
model = Project
class OrderFilterSet(FilterSet):
id = AllLookupsFilter()
contractor = RelatedFilter('users.filters.UserFilterSet')
team = RelatedFilter('users.filters.TeamFilterSet')
class AnswerFilterSet(FilterSet):
budget = 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')
teams = RelatedFilter('users.filters.TeamFilterSet')
class Meta:
model = Answer
class OrderFilterSet(FilterSet):
created = AllLookupsFilter()
id = AllLookupsFilter()
secure = AllLookupsFilter()
status = AllLookupsFilter()
contractor = RelatedFilter('users.filters.UserFilterSet')
project = RelatedFilter('projects.filters.ProjectFilterSet')
team = RelatedFilter('users.filters.TeamFilterSet')
class Meta:
model = Order
class StageFilterSet(FilterSet):
cost = AllLookupsFilter()
cost_type = AllLookupsFilter()
id = AllLookupsFilter()
is_paid = AllLookupsFilter()
name = AllLookupsFilter()
pos = AllLookupsFilter()
result = AllLookupsFilter()
cost = AllLookupsFilter()
status = AllLookupsFilter()
term = AllLookupsFilter()
cost_type = AllLookupsFilter()
term_type = AllLookupsFilter()
status = AllLookupsFilter()
pos = AllLookupsFilter()
order = RelatedFilter('projects.filters.OrderFilterSet')
is_paid = AllLookupsFilter()
class Meta:
model = Stage
@ -87,6 +127,7 @@ class RealtyFilterSet(FilterSet):
class PortfolioPhotoFilterSet(FilterSet):
id = AllLookupsFilter()
portfolio = RelatedFilter('projects.filters.PortfolioFilterSet')
# img = ???

@ -207,20 +207,23 @@ class PortfolioEditForm(forms.ModelForm):
fields = (
'building_classification',
'construction_type',
'specialization',
'budget',
'currency',
'description',
'name',
'photos',
'term',
'term_type',
'worksell',
)
# fields = '__all__'
#
# widgets = {
# 'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
# 'building_classification': forms.Select(attrs={'class': 'selectpicker'}),
# 'currency': forms.Select(attrs={'class': 'selectpicker'}),
# 'term_type': forms.Select(attrs={'class': 'selectpicker'}),
# }
widgets = {
'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
'building_classification': forms.Select(attrs={'class': 'selectpicker'}),
'currency': forms.Select(attrs={'class': 'selectpicker'}),
'term_type': forms.Select(attrs={'class': 'selectpicker'}),
}
def __init__(self, *args, **kwargs):
# 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):
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)
class Meta:
@ -189,7 +189,7 @@ class AnswerMessage(models.Model):
class AnswerFile(models.Model):
answer = models.ForeignKey(Answer, related_name='files', blank=True, null=True)
name = models.CharField(max_length=255)
file = models.FileField(upload_to='projects/answer_files')
file = models.FileField(upload_to='projects/answer_files/')
class Meta:
verbose_name = 'Файл для отклика'
@ -344,7 +344,7 @@ class Portfolio(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')
class Meta:

@ -3,11 +3,40 @@ from generic_relations.relations import GenericRelatedField
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 common.serializers import LocationSerializer
from common.serializers import LocationSerializer, ContentTypeSerializer
from specializations.serializers import SpecializationSerializer
from users.models import User, Team
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 Meta:
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):
answers = AnswerSerializer_(many=True)
customer = UserSerializer()
specialization = SpecializationSerializer()
realty = RealtySerializer()
order = OrderSerializer_() # TODO: Can't serialize a reverse/reciprocal relation
realty = RealtySerializer()
specialization = SpecializationSerializer()
class Meta:
model = Project
fields = (
'answers',
'budget',
'budget_by_agreement',
'created',
@ -158,25 +224,10 @@ class OrderSerializer(ModelSerializer):
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):
# answers = AnswerSerializer(many=True)
building_classification = BuildingClassficationSerializer()
construction_type = ConstructionTypeSerializer()
id = ReadOnlyField()
location = LocationSerializer()
photos = PortfolioPhotoSerializer(many=True)
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):
content_type = ContentTypeSerializer()
files = AnswerFileSerializer(many=True)
portfolios = PortfolioSerializer(many=True)
project = ProjectSerializer
project = ProjectSerializer()
author = GenericRelatedField({
User: UserSerializer(),
@ -236,15 +274,18 @@ class AnswerSerializer(ModelSerializer):
'created',
'currency',
'id',
'is_archive',
'object_id',
'rejected',
'secure_deal_only',
'term',
'term_type',
'text',
'author', # Generic related field
'content_type',
'files',
'portfolios',
'project',
'author', # Generic related field
# 'candidates',
)

@ -109,7 +109,7 @@
<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 href="#" onclick="toggleProjectAnswerForm(); return false" class="new-answer">
Ответить на проект
</a>
@ -283,6 +283,18 @@
<input type="hidden" name="next" value="{{ request.path }}">
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink3" title="{{ answer.pk }}">отказ</a>
</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>
@ -361,7 +373,7 @@
</div>
</div>
{% 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 %}
<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 %}">
@ -655,6 +667,12 @@
предложить проект
</a>
</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 %}
<form action="{% url 'projects:reject-project-answer' pk=answer.pk %}" method="POST" novalidate>
@ -843,6 +861,12 @@
предложить проект
</a>
</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 %}
<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 }}">
<a href="#" onclick="$(this).closest('form').submit(); return false" class="candLink candLink1" title="{{ answer.pk }}">Восстановить</a>
</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 class="col-xs-12">
@ -1083,9 +1113,10 @@
{% block js_block %}
<script>
(function() {
// Persistent Bootstrap tabs --------------------------------------
// Persistent Bootstrap tabs --------------------------------------
;(function() {
var $allTabs = $('a[data-toggle="tab"]')
var $tab = $allTabs.filter('[href="' + window.location.hash + '"]').first()
@ -1104,5 +1135,31 @@
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>
{% endblock %}

@ -47,7 +47,7 @@ urlpatterns = [
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/(?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+)/trash/$', ContractorPortfolioTrashView.as_view(), name='contractor-portfolio-trash'),

@ -762,7 +762,7 @@ class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
template_name = 'contractor_portfolio_edit.html'
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):
portfolio = form.instance
@ -781,11 +781,9 @@ class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
live_images = form.cleaned_data['live_images']
# import code; code.interact(local=dict(globals(), **locals()))
for live_image in live_images:
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)

@ -4,15 +4,15 @@ from .models import Review
class ReviewFilterSet(FilterSet):
created = AllLookupsFilter()
id = AllLookupsFilter()
text = AllLookupsFilter()
type = AllLookupsFilter()
created = AllLookupsFilter()
from_customer = 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_customer = RelatedFilter('users.filters.UserFilterSet')
class Meta:
model = Review

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

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

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

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

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

@ -1,8 +1,8 @@
import itertools
from django import forms
from common.models import Location
from .models import WorkSell
from common.models import Location, LiveImageUpload
from .models import WorkSell, WorkSellPhoto
from specializations.models import Specialization
@ -17,19 +17,32 @@ class ContractorWorkSellTrashForm(forms.Form):
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:
model = WorkSell
fields = (
'budget',
'building_classification',
'construction_type',
'contractor',
'specialization',
'budget',
'currency',
'description',
'location',
'name',
'specialization',
'term',
'term_type',
)
@ -43,10 +56,8 @@ class WorkSellForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['specialization'].queryset = Specialization.objects.root_nodes()[0].get_descendants()
self.fields['location'].queryset = Location.objects.root_nodes()[0].get_descendants()
# self.fields['location'].queryset = Location.objects # Migrate with this enabled
self.fields['photos'].queryset = self.instance.photos.all()
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):
img = ImageField(upload_to='worksell/worksell')
img = ImageField(upload_to='worksell/worksell/')
worksell = models.ForeignKey(WorkSell, related_name='photos')
def __str__(self):
return self.worksell.name
class Meta:
verbose_name = 'Изображение Готовая работа'
verbose_name_plural = 'Изображения Готовые работы'
def __str__(self):
return self.img and self.img.url or str(self.img)
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)
def __str__(self):

@ -1,6 +1,21 @@
{% 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 %}
{% include 'partials/header.html' %}
@ -26,7 +41,8 @@
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Специализации</p>
</div>
<div class="-spec-select-container">
<div class="col-lg-3">
<input type='hidden' class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
@ -43,9 +59,9 @@
<input type='hidden' class="-spec-select -spec-select-level-4" style="width: 100%">
</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 }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ form.budget.errors.as_text }}</p>
@ -82,6 +98,47 @@
<p>Классификация здания</p>
{{ form.building_classification}}
</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">

@ -178,13 +178,35 @@ class WorkSellUpdateView(UpdateView):
template_name = 'worksell_edit.html'
def dispatch(self, request, *args, **kwargs):
# import code; code.interact(local=dict(globals(), **locals()))
if self.get_object().contractor_id != request.user.pk:
return HttpResponseForbidden('403 Forbidden')
return super().dispatch(request, *args, **kwargs)
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):

Loading…
Cancel
Save