optimizations

remotes/origin/PR-39
PekopT 9 years ago
parent da3793495d
commit 669d1d5db0
  1. 1
      api/test.py
  2. 1
      api/urls.py
  3. 78
      api/views.py
  4. 12
      archilance/management/commands/generate_build_classifs.py
  5. 12
      archilance/management/commands/generate_constr_types.py
  6. 23
      archilance/management/commands/generate_locations.py
  7. 13
      archilance/management/commands/generate_perm_groups.py
  8. 38
      archilance/management/commands/generate_portfolios.py
  9. 46
      archilance/management/commands/generate_projects.py
  10. 28
      archilance/management/commands/generate_realties.py
  11. 26
      archilance/management/commands/generate_reviews.py
  12. 22
      archilance/management/commands/generate_specializations.py
  13. 12
      archilance/management/commands/generate_superuser.py
  14. 33
      archilance/management/commands/generate_teams.py
  15. 47
      archilance/management/commands/generate_users.py
  16. 31
      archilance/management/commands/generate_work_sells.py
  17. 16
      archilance/middlewares.py
  18. 17
      archilance/mixins.py
  19. 8
      archilance/settings/base.py
  20. 78
      archilance/urls.py
  21. 68
      archilance/util.py
  22. 36
      archilance/views.py
  23. 6
      archilance/wsgi.py
  24. 21
      assets/css/main.css
  25. BIN
      assets/img/skype.png
  26. BIN
      assets/img/user-4.png
  27. BIN
      assets/img/user-5.png
  28. 8
      assets/index.js
  29. 5
      chat/admin.py
  30. 27
      chat/chat.py
  31. 9
      chat/filters.py
  32. 1
      chat/forms.py
  33. 2
      chat/models.py
  34. 1
      chat/response.py
  35. 10
      chat/serializers.py
  36. 3
      chat/settings.example.py
  37. 2
      chat/tests.py
  38. 1
      chat/utils.py
  39. 60
      chat/views.py
  40. 21
      common/admin.py
  41. 4
      common/filters.py
  42. 14
      common/forms.py
  43. 7
      common/middleware.py
  44. 12
      common/models.py
  45. 4
      common/serializers.py
  46. 2
      common/tests.py
  47. 5
      common/urls.py
  48. 42
      common/views.py
  49. 8
      projects/admin.py
  50. 3
      projects/apps.py
  51. 31
      projects/filters.py
  52. 159
      projects/forms.py
  53. BIN
      projects/locale/ru_RU/LC_MESSAGES/django.mo
  54. 48
      projects/locale/ru_RU/LC_MESSAGES/django.po
  55. 38
      projects/migrations/0047_auto_20161014_2349.py
  56. 97
      projects/models.py
  57. 71
      projects/serializers.py
  58. 6
      projects/signals.py
  59. 498
      projects/templates/_trash/project_form.html
  60. 28
      projects/templates/_trash/testport.html
  61. 8
      projects/templates/chattest.html
  62. 296
      projects/templates/comparison.html
  63. 249
      projects/templates/contractor_portfolio_edit.html
  64. 716
      projects/templates/customer_project_create.html
  65. 719
      projects/templates/customer_project_edit.html
  66. 99
      projects/templates/partials/modals/project_work_type_suggestion.html
  67. 155
      projects/templates/portfolio_create.html
  68. 2465
      projects/templates/project_detail.html
  69. 435
      projects/templates/project_filter.html
  70. 20
      projects/templatetags/project_tags.py
  71. 2
      projects/tests.py
  72. 30
      projects/urls.py
  73. 141
      projects/views.py
  74. 1
      ratings/admin.py
  75. 6
      ratings/models.py
  76. 2
      ratings/tests.py
  77. 2
      ratings/views.py
  78. 1
      reviews/admin.py
  79. 2
      reviews/apps.py
  80. 2
      reviews/filters.py
  81. 3
      reviews/forms.py
  82. 6
      reviews/models.py
  83. 4
      reviews/serializers.py
  84. 2
      reviews/signals.py
  85. 2
      reviews/tests.py
  86. 6
      reviews/views.py
  87. 8
      specializations/admin.py
  88. 6
      specializations/filters.py
  89. 21
      specializations/migrations/0003_specialization_order.py
  90. 24
      specializations/migrations/0004_auto_20161014_2344.py
  91. 14
      specializations/models.py
  92. 8
      specializations/serializers.py
  93. 2
      specializations/tests.py
  94. 1
      specializations/urls.py
  95. 19
      specializations/views.py
  96. 6
      users/admin.py
  97. 2
      users/apps.py
  98. 2
      users/backend.py
  99. 21
      users/filters.py
  100. 75
      users/forms.py
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,4 +3,3 @@ from rest_framework.test import APITestCase
class FileUploadTests(APITestCase):
pass

@ -22,7 +22,6 @@ from .views import (
WorkSellViewSet,
)
router = routers.DefaultRouter()
router.register(r'answers', AnswerViewSet)

@ -4,41 +4,33 @@ from rest_framework import permissions
from rest_framework.pagination import PageNumberPagination
from rest_framework.viewsets import ModelViewSet
from projects.models import Project, Realty, Stage, Portfolio, PortfolioPhoto, Answer, AnswerFile, Order
from projects.serializers import (
ProjectSerializer, RealtySerializer, StageSerializer, PortfolioSerializer,
PortfolioPhotoSerializer, AnswerSerializer, OrderSerializer, AnswerFileSerializer,
)
from chat.filters import MessageFilterSet, NoteFilterSet, DocumentFilterSet
from chat.models import Message, Notes, Documents, NewMessage
from chat.serializers import MessageSerializer, NoteSerializer, DocumentsSerializer
from common.filters import LocationFilterSet
from common.models import Location
from common.serializers import LocationSerializer
from projects.filters import (
ProjectFilterSet, RealtyFilterSet, StageFilterSet, PortfolioFilterSet, OrderFilterSet,
PortfolioPhotoFilterSet, AnswerFilterSet,
)
from projects.models import Project, Realty, Stage, Portfolio, PortfolioPhoto, Answer, Order
from projects.serializers import (
ProjectSerializer, RealtySerializer, StageSerializer, PortfolioSerializer,
PortfolioPhotoSerializer, AnswerSerializer, OrderSerializer, )
from reviews.filters import ReviewFilterSet
from reviews.models import Review
from reviews.serializers import ReviewSerializer
from specializations.filters import SpecializationFilterSet
from specializations.models import Specialization
from specializations.serializers import SpecializationSerializer
from specializations.filters import SpecializationFilterSet
from users.filters import UserFilterSet, TeamFilterSet
from users.models import User, ContractorResumeFiles, ContractorResume, Team
from users.serializers import UserSerializer, ContractorResumeFilesSerializer, ContractorResumeSerializer, TeamSerializer
from users.filters import UserFilterSet, TeamFilterSet, ContractorResumeFilesFilterSet, ContractorResumeFilterSet
from common.models import Location
from common.serializers import LocationSerializer
from common.filters import LocationFilterSet
from chat.models import Message, Notes, Documents, NewMessage
from chat.serializers import MessageSerializer, NoteSerializer, DocumentsSerializer
from chat.filters import MessageFilterSet, NoteFilterSet, DocumentFilterSet
from reviews.models import Review
from reviews.serializers import ReviewSerializer
from reviews.filters import ReviewFilterSet
from users.serializers import UserSerializer, ContractorResumeFilesSerializer, ContractorResumeSerializer, \
TeamSerializer
from work_sell.filters import WorkSellFilterSet, WorkSellPhotoFilterSet
from work_sell.models import WorkSell, WorkSellPhoto
from work_sell.serializers import WorkSellSerializer, WorkSellPhotoSerializer
from work_sell.filters import WorkSellFilterSet, WorkSellPhotoFilterSet
class ContractorResumeFilesViewSet(ModelViewSet):
@ -83,7 +75,9 @@ class DocumentViewSet(ModelViewSet):
if search_param:
# import code; code.interact(local=dict(globals(), **locals()))
if search_param == 'in':
queryset = queryset.filter(Q(sender__in=[sender_id, recipent_id]),Q(recipent__in=[sender_id, recipent_id])).filter(order__isnull=True).order_by('created')
queryset = queryset.filter(Q(sender__in=[sender_id, recipent_id]),
Q(recipent__in=[sender_id, recipent_id])).filter(
order__isnull=True).order_by('created')
return queryset
@ -91,16 +85,17 @@ class ProjectViewSet(ModelViewSet):
queryset = Project.objects.all()
serializer_class = ProjectSerializer
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
@ -118,7 +113,8 @@ class NoteViewSet(ModelViewSet):
if search_param:
# import code; code.interact(local=dict(globals(), **locals()))
if search_param == 'in':
queryset = queryset.filter(Q(sender__in=[sender_id,recipent_id]),Q(recipent__in=[sender_id,recipent_id])).order_by('created')
queryset = queryset.filter(Q(sender__in=[sender_id, recipent_id]),
Q(recipent__in=[sender_id, recipent_id])).order_by('created')
return queryset
@ -135,11 +131,11 @@ class MessageViewSet(ModelViewSet):
if search_param:
# import code; code.interact(local=dict(globals(), **locals()))
if search_param == 'in':
queryset = queryset.filter(Q(sender__in=[sender_id,recipent_id]),Q(recipent__in=[sender_id,recipent_id])).\
filter(order__isnull=True).\
queryset = queryset.filter(Q(sender__in=[sender_id, recipent_id]),
Q(recipent__in=[sender_id, recipent_id])). \
filter(order__isnull=True). \
filter(~Q(sender=F('recipent')))
queryset = queryset.order_by('created')
return queryset
@ -187,8 +183,8 @@ class LocationViewSet(ModelViewSet):
class PortfolioPagination(PageNumberPagination):
page_size = settings.API_PAGE_SIZE # Default page size
page_size_query_param = 'page_size' # Provide custom page size through a query param
page_size = settings.API_PAGE_SIZE # Default page size
page_size_query_param = 'page_size' # Provide custom page size through a query param
max_page_size = 1000
@ -205,13 +201,12 @@ class PortfolioPhotoViewSet(ModelViewSet):
filter_class = PortfolioPhotoFilterSet
class WorkSellPagination(PageNumberPagination):
page_size = settings.API_PAGE_SIZE # Default page size
page_size_query_param = 'page_size' # Provide custom page size through a query param
page_size = settings.API_PAGE_SIZE # Default page size
page_size_query_param = 'page_size' # Provide custom page size through a query param
max_page_size = 1000
class WorkSellViewSet(ModelViewSet):
queryset = WorkSell.objects.all()
serializer_class = WorkSellSerializer
@ -219,15 +214,12 @@ class WorkSellViewSet(ModelViewSet):
pagination_class = WorkSellPagination
class WorkSellPhotoViewSet(ModelViewSet):
queryset = WorkSellPhoto.objects.all()
serializer_class = WorkSellPhotoSerializer
filter_class = WorkSellPhotoFilterSet
class AnswerViewSet(ModelViewSet):
queryset = Answer.objects.all()
serializer_class = AnswerSerializer

@ -1,11 +1,9 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from projects.models import BuildingClassfication
@ -14,5 +12,5 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating building classifications...')
print('---------------------------------------')
_.times(lambda i: BuildingClassfication.objects.create(name='Build. classif. %s' % i), 100)

@ -1,11 +1,9 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from projects.models import ConstructionType
@ -14,5 +12,5 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating construction types...')
print('---------------------------------------')
_.times(lambda i: ConstructionType.objects.create(name='Constr. type %s' % i), 100)

@ -1,11 +1,9 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from common.models import Location
@ -14,15 +12,14 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating locations...')
print('---------------------------------------')
_root = Location.objects.create(name='_root', type='_root')
depths = ('A','B','C','D')
depths = ('A', 'B', 'C', 'D')
for d1 in depths:
x = Location.objects.create(name='Страна %s' % d1, type='country', parent=_root)
for d2 in depths:
y = Location.objects.create(name='Регион %s-%s' % (d1,d2), type='region', parent=x)
y = Location.objects.create(name='Регион %s-%s' % (d1, d2), type='region', parent=x)
for d3 in depths:
z = Location.objects.create(name='Город %s-%s-%s' % (d1,d2,d3), type='town', parent=y)
z = Location.objects.create(name='Город %s-%s-%s' % (d1, d2, d3), type='town', parent=y)

@ -1,11 +1,9 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.contrib.auth.models import Group
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
class Command(BaseCommand):
@ -13,7 +11,6 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating permission groups...')
print('---------------------------------------')
Group.objects.create(name='Исполнители')
Group.objects.create(name='Заказчики')

@ -1,15 +1,14 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
_.map = _.map_;
_.filter = _.filter_
from archilance import util
from common.models import Location
from projects.models import Portfolio, PortfolioPhoto, CURRENCIES, TERM_TYPES, BuildingClassfication , ConstructionType
from projects.models import Portfolio, CURRENCIES, TERM_TYPES, BuildingClassfication, ConstructionType
from specializations.models import Specialization
from users.models import User, Team, GENDERS
from users.models import User
class Command(BaseCommand):
@ -17,8 +16,7 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating portfolios...')
print('---------------------------------------')
# ('photos', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('budget', 'Relation? False', 'Null? True', 'Blank? True', 'Hidden? False'),
# ('building_classification', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
@ -33,8 +31,8 @@ class Command(BaseCommand):
# ('term_type', 'Relation? False', 'Null? True', 'Blank? True', 'Hidden? False'),
# ('user', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('worksell', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
def create_portfolio(i):
portf = Portfolio(
budget=util.random_amount(),
@ -46,24 +44,26 @@ class Command(BaseCommand):
term_type=_.sample(TERM_TYPES)[0],
worksell=_.sample((True, False)),
)
portf.save()
portf.user = User.contractor_objects.order_by('?').first()
portf.building_classification = BuildingClassfication.objects.order_by('?').first()
portf.construction_type = ConstructionType.objects.order_by('?').first()
portf.location = Location.objects.root_nodes()[0].get_descendants().order_by('?').first()
portf.specialization = Specialization.objects.root_nodes()[0].get_descendants().order_by('?').first()
pic_names = _.split('a.png b.png c.jpg d.png e.jpg f.png g.png h.jpg i.png j.png k.jpg l.png m.png n.png o.png p.png q.jpg', ' ')
pic_names = _.split(
'a.png b.png c.jpg d.png e.jpg f.png g.png h.jpg i.png j.png k.jpg l.png m.png n.png o.png p.png q.jpg',
' ')
_.times(
lambda i_: portf.photos.create(img='_sample_files/%s' % util.take_one_random(pic_names)),
_.random(1, 15),
)
portf.save()
return portf
_.times(create_portfolio, 1000)

@ -1,9 +1,8 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
_.map = _.map_;
_.filter = _.filter_
from archilance import util
from projects.models import Project, Order, CURRENCIES, TERM_TYPES, Specialization, Realty
@ -15,23 +14,22 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating projects...')
print('---------------------------------------')
# Fields:
# ('customer', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
# ('specialization', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
# ('answers', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('candidates', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('files', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('order', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('realty', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('reviews', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('budget', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('created', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('currency', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
@ -41,16 +39,16 @@ class Command(BaseCommand):
# ('term', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('term_type', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('work_type', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('budget_by_agreement', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('cro', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('id', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('price_and_term_required', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('text', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
def create_project(i):
project = Project(
budget=util.random_amount(),
@ -67,20 +65,20 @@ class Command(BaseCommand):
work_type=_.sample(Project.WORK_TYPES)[0],
state='active',
)
project.specialization = Specialization.objects.root_nodes()[0].get_descendants().order_by('?').first()
project.realty = Realty.objects.order_by('?').first()
project.customer = User.customer_objects.filter(is_active=True).order_by('?').first()
project.save()
Order.objects.create(
project=project,
contractor=_.sample((None, User.contractor_objects.order_by('?').first())),
secure=_.sample((True, False)),
status=_.sample(Order.STATUSES)[0],
)
return project
_.times(create_project, 1000)
_.times(create_project, 1000)

@ -1,11 +1,9 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from common.models import Location
from projects.models import Realty, BuildingClassfication, ConstructionType
from users.models import User
@ -16,8 +14,7 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating realties...')
print('---------------------------------------')
# # Fields:
#
#
@ -31,17 +28,18 @@ class Command(BaseCommand):
#
#
# ('name', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
def create_realty(i):
realty = Realty(name='Realty %s' % i)
realty.building_classification = BuildingClassfication.objects.order_by('?').first()
realty.construction_type = ConstructionType.objects.order_by('?').first()
realty.location = Location.objects.root_nodes()[0].get_descendants().order_by('?').first()
realty.user = User.objects.filter(groups__name='Заказчики', is_active=True, is_superuser=False).order_by('?').first()
realty.user = User.objects.filter(groups__name='Заказчики', is_active=True, is_superuser=False).order_by(
'?').first()
realty.save()
return realty
_.times(create_realty, 100)
_.times(create_realty, 100)

@ -1,14 +1,12 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
_.map = _.map_;
_.filter = _.filter_
from archilance import util
from projects.models import Project, Portfolio
from specializations.models import Specialization
from users.models import User, GENDERS, Team
from projects.models import Project
from users.models import User
from reviews.models import Review
@ -17,25 +15,25 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating reviews...')
print('---------------------------------------')
def create_review(i):
review = Review()
review.project = Project.objects.order_by('?').first()
review.stars = _.random(1, 5)
review.text = util.lorem()
review.is_secured = _.sample((True, False))
review.save()
if _.sample((True, False)):
review.from_contractor = User.contractor_objects.order_by('?').first()
review.target_customer = User.customer_objects.order_by('?').first()
else:
review.from_customer = User.customer_objects.order_by('?').first()
review.target_contractor = User.contractor_objects.order_by('?').first()
review.save()
return review
_.times(create_review, 300)

@ -1,8 +1,9 @@
import pydash as _;
from django.core.management import BaseCommand
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from specializations.models import Specialization
@ -11,17 +12,16 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating specializations...')
print('---------------------------------------')
_root = Specialization.objects.create(name='_root')
stages = ('A','B','C','D')
stages = ('A', 'B', 'C', 'D')
for s1 in stages:
x = Specialization.objects.create(name='Специализация %s' % s1, parent=_root)
for s2 in stages:
y = Specialization.objects.create(name='Специализация %s-%s' % (s1,s2), parent=x)
y = Specialization.objects.create(name='Специализация %s-%s' % (s1, s2), parent=x)
for s3 in stages:
z = Specialization.objects.create(name='Специализация %s-%s-%s' % (s1,s2,s3), parent=y)
z = Specialization.objects.create(name='Специализация %s-%s-%s' % (s1, s2, s3), parent=y)
for s4 in stages:
Specialization.objects.create(name='Специализация %s-%s-%s-%s' % (s1,s2,s3,s4), parent=z)
Specialization.objects.create(name='Специализация %s-%s-%s-%s' % (s1, s2, s3, s4), parent=z)

@ -1,11 +1,9 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from users.models import User
@ -14,5 +12,5 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating superusers...')
print('---------------------------------------')
User.objects.create_superuser('admin@example.com', '123456')

@ -1,14 +1,13 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
_.map = _.map_;
_.filter = _.filter_
from archilance import util
from projects.models import Portfolio
from specializations.models import Specialization
from users.models import User, GENDERS, Team
from users.models import User, Team
class Command(BaseCommand):
@ -16,31 +15,31 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating teams...')
print('---------------------------------------')
# ('name', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('portfolios', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('owner', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('users', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
# ('specializations', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
contractors = list(User.contractor_objects.order_by('?'))
owners = util.take(contractors, len(contractors) // 2)
members = contractors
i = 0
for owner in owners:
team = Team(name='Team %s' % i)
team.save()
team.owner = owner
team.contractors = _.sample(members, _.random(1, 5))
team.specializations = Specialization.objects.root_nodes()[0].get_descendants().order_by('?')[:_.random(1, 5)]
team.specializations = Specialization.objects.root_nodes()[0].get_descendants().order_by('?')[
:_.random(1, 5)]
team.portfolios = Portfolio.objects.order_by('?')[:_.random(1, 10)]
team.save()
i += 1

@ -1,14 +1,13 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.contrib.auth.models import Group
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
from archilance import util
_.map = _.map_;
_.filter = _.filter_
from common.models import Location
from specializations.models import Specialization
from users.models import User, GENDERS
from users.models import User
class Command(BaseCommand):
@ -16,15 +15,13 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating users...')
print('---------------------------------------')
# # Fields:
#
# ('contractor_specializations', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
# ('groups', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
# ('user_permissions', 'Relation? True', 'Null? False', '(relation)', 'Hidden? False'),
# ('answers', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('contractor_financial_info', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('location', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
@ -47,9 +44,9 @@ class Command(BaseCommand):
# ('User_groups+', 'Relation? True', 'Null? True', '(relation)', 'Hidden? True'),
# ('User_user_permissions+', 'Relation? True', 'Null? True', '(relation)', 'Hidden? True'),
# ('work_sell', 'Relation? True', 'Null? True', '(relation)', 'Hidden? False'),
# ('contractor_status', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('created', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('data_joined', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
@ -57,7 +54,7 @@ class Command(BaseCommand):
# ('last_time_visit', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('password', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('username', 'Relation? False', 'Null? False', 'Blank? False', 'Hidden? False'),
# ('avatar', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('cro', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('date_of_birth', 'Relation? False', 'Null? True', 'Blank? True', 'Hidden? False'),
@ -72,12 +69,12 @@ class Command(BaseCommand):
# ('phone', 'Relation? False', 'Null? True', 'Blank? True', 'Hidden? False'),
# ('skype', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
# ('website', 'Relation? False', 'Null? False', 'Blank? True', 'Hidden? False'),
def create_user(i):
username = 'user-%s' % i
return User.objects.create(
first_name='Василий',
last_name='Пупкин',
@ -88,17 +85,17 @@ class Command(BaseCommand):
contractor_status=_.sample(User.STATUSES)[0],
cro=_.sample((True, False)),
)
users = _.times(create_user, 500)
contractor_group = Group.objects.get(name='Исполнители')
customer_group = Group.objects.get(name='Заказчики')
for user in users:
user.set_password('123')
user.groups.add(customer_group if user.pk % 2 == 0 else contractor_group)
user.contractor_specializations = Specialization.objects.root_nodes()[0].get_descendants().order_by('?')[:_.random(1, 5)]
user.contractor_specializations = Specialization.objects.root_nodes()[0].get_descendants().order_by('?')[
:_.random(1, 5)]
user.location = Location.objects.root_nodes()[0].get_descendants().order_by('?').first()
user.save()

@ -1,16 +1,15 @@
from django.contrib.auth.models import Group, Permission
from django.contrib.contenttypes.models import ContentType
import pydash as _;
from django.core.management import BaseCommand
from django.utils import timezone
import pydash as _; _.map = _.map_; _.filter = _.filter_
import random
_.map = _.map_;
_.filter = _.filter_
from archilance import util
from common.models import Location
from projects.models import TERM_TYPES, CURRENCIES, BuildingClassfication, ConstructionType
from specializations.models import Specialization
from users.models import User, Team
from work_sell.models import WorkSell, WorkSellPhoto
from work_sell.models import WorkSell
class Command(BaseCommand):
@ -18,7 +17,7 @@ class Command(BaseCommand):
print('---------------------------------------')
print('Generating work sells...')
print('---------------------------------------')
def create_work_sell(i):
ws = WorkSell(
budget=util.random_amount(),
@ -29,27 +28,29 @@ class Command(BaseCommand):
term=_.random(0, 20),
term_type=_.sample(TERM_TYPES)[0],
)
ws.save()
if _.sample((True, False)):
ws.contractor = User.contractor_objects.order_by('?').first()
else:
ws.team = Team.objects.order_by('?').first()
ws.building_classification = BuildingClassfication.objects.order_by('?').first()
ws.construction_type = ConstructionType.objects.order_by('?').first()
ws.location = Location.objects.root_nodes()[0].get_descendants().order_by('?').first()
ws.specialization = Specialization.objects.root_nodes()[0].get_descendants().order_by('?').first()
pic_names = _.split('a.png b.png c.jpg d.png e.jpg f.png g.png h.jpg i.png j.png k.jpg l.png m.png n.png o.png p.png q.jpg', ' ')
pic_names = _.split(
'a.png b.png c.jpg d.png e.jpg f.png g.png h.jpg i.png j.png k.jpg l.png m.png n.png o.png p.png q.jpg',
' ')
_.times(
lambda i_: ws.photos.create(img='_sample_files/%s' % util.take_one_random(pic_names)),
_.random(1, 15),
)
ws.save()
return ws
_.times(create_work_sell, 1000)

@ -0,0 +1,16 @@
from django.utils import translation
class LocaleMiddleware(object):
"""
This is a very simple middleware that parses a request
and decides what translation object to install in the current
thread context. This allows pages to be dynamically
translated to the language the user desires (if the language
is available, of course).
"""
def process_request(self, request):
user_language = 'ru_RU'
translation.activate(user_language)
request.session[translation.LANGUAGE_SESSION_KEY] = user_language

@ -6,21 +6,20 @@ from django.views.generic.base import ContextMixin
class BaseMixin(ContextMixin):
def get_context_data(self, **kwargs):
c = super().get_context_data(**kwargs)
pk = self.kwargs.get('pk') # Current object's ID
next = self.kwargs.get('next') # Redirect next path
back = self.kwargs.get('back') # Redirect back path
pk = self.kwargs.get('pk') # Current object's ID
next = self.kwargs.get('next') # Redirect next path
back = self.kwargs.get('back') # Redirect back path
if pk: c['pk'] = int(pk)
if next: c['next'] = next
if back: c['back'] = back
c['domain'] = Site.objects.get_current().domain
c['TEMPLATE_DEBUG'] = getattr(settings, 'TEMPLATE_DEBUG', None)
c['TESTING'] = getattr(settings, 'TESTING', None)
return c
return c
# import code; code.interact(local=dict(globals(), **locals()))

@ -83,6 +83,7 @@ INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS + WAGTAIL
MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'archilance.middlewares.LocaleMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
@ -102,7 +103,6 @@ TEMPLATES = [
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(ROOT_DIR, 'templates')],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
@ -237,7 +237,7 @@ FIELDS_STORED_IN_SESSION = ['user_type']
# Internationalization
# https://docs.djangoproject.com/en/1.9/topics/i18n/
LANGUAGE_CODE = 'ru-ru'
LANGUAGE_CODE = 'ru_RU'
TIME_ZONE = 'Europe/Moscow'
@ -247,6 +247,10 @@ USE_L10N = True
USE_TZ = True
LANGUAGES = (
('ru_RU', 'Russian'),
)
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.9/howto/static-files/

@ -4,52 +4,50 @@ from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.views.generic import TemplateView
from wagtail.wagtailadmin import urls as wagtailadmin_urls
from wagtail.wagtailcore import urls as wagtail_urls
from .views import HomeTemplateView, TestChatTemplateView, TestView
from common.views import CustomRegistrationView
from wallets.views import TmpCheckOrderView, TmpPaymentAvisoView
from .views import HomeTemplateView, TestChatTemplateView, TestView
from wagtail.wagtailadmin import urls as wagtailadmin_urls
from wagtail.wagtailcore import urls as wagtail_urls
urlpatterns = [
url(r'^$', HomeTemplateView.as_view()),
url('', include('social.apps.django_app.urls', namespace='social')),
url(r'^chattest/$', TestChatTemplateView.as_view()),
url(r'^work_sell/', include('work_sell.urls')),
url(r'^test/$', TestView.as_view(), name='test'),
url(r'^projects/', include('projects.urls')),
url(r'^reviews/', include('reviews.urls')),
url(r'^wallets/', include('wallets.urls')),
url(r'^chat/', include('chat.urls')),
url(r'^specializations/', include('specializations.urls')),
url(r'^users/', include('users.urls')),
url(r'^common/', include('common.urls')),
url(r'^password/', include('password_reset.urls')),
url(r'^users/register/$', CustomRegistrationView.as_view(), name='registration_register'),
url(r'^users/', include('registration.backends.default.urls')),
urlpatterns = [
url(r'^$', HomeTemplateView.as_view()),
url('', include('social.apps.django_app.urls', namespace='social')),
url(r'^chattest/$', TestChatTemplateView.as_view()),
url(r'^work_sell/', include('work_sell.urls')),
url(r'^test/$', TestView.as_view(), name='test'),
url(r'^projects/', include('projects.urls')),
url(r'^reviews/', include('reviews.urls')),
url(r'^wallets/', include('wallets.urls')),
url(r'^chat/', include('chat.urls')),
url(r'^specializations/', include('specializations.urls')),
url(r'^users/', include('users.urls')),
url(r'^common/', include('common.urls')),
url(r'^password/', include('password_reset.urls')),
url(r'^users/register/$', CustomRegistrationView.as_view(), name='registration_register'),
url(r'^users/', include('registration.backends.default.urls')),
url(r'^admin/', admin.site.urls),
url(r'^api/', include('api.urls')),
url(r'^cms/', include(wagtailadmin_urls)),
url(r'^pages/', include(wagtail_urls)),
# TODO: Move URLs into the "wallets" app (Yandex Money settings modification needed):
url(r'^yandex/check$', TmpCheckOrderView.as_view(), name='tmp-check-order'),
url(r'^yandex/aviso$', TmpPaymentAvisoView.as_view(), name='tmp-payment-aviso'),
url(r'^payment/success$', TemplateView.as_view(template_name='message.html'), {
'message': 'Оплата прошла успешно',
}, name='tmp-payment-success'),
url(r'^payment/fail$', TemplateView.as_view(template_name='message.html'), {
'message': 'Произошла ошибка при оплате',
}, name='tmp-payment-fail'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
url(r'^admin/', admin.site.urls),
url(r'^api/', include('api.urls')),
url(r'^cms/', include(wagtailadmin_urls)),
url(r'^pages/', include(wagtail_urls)),
# TODO: Move URLs into the "wallets" app (Yandex Money settings modification needed):
url(r'^yandex/check$', TmpCheckOrderView.as_view(), name='tmp-check-order'),
url(r'^yandex/aviso$', TmpPaymentAvisoView.as_view(), name='tmp-payment-aviso'),
url(r'^payment/success$', TemplateView.as_view(template_name='message.html'), {
'message': 'Оплата прошла успешно',
}, name='tmp-payment-success'),
url(r'^payment/fail$', TemplateView.as_view(template_name='message.html'), {
'message': 'Произошла ошибка при оплате',
}, name='tmp-payment-fail'),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
if settings.DEBUG:
urlpatterns += staticfiles_urlpatterns()

@ -1,12 +1,16 @@
import datetime
from pprint import pprint
import natsort
import pydash as _;
from django.core import validators
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.storage import FileSystemStorage
from django.shortcuts import _get_queryset
from django.utils import timezone
from pprint import pprint, pformat
import datetime
import natsort
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
import random
import string
@ -20,20 +24,20 @@ def take(coll, n):
def take_random(coll, n):
if n == 0:
return []
chunk = _.sample(coll, n)
for item in chunk:
coll.remove(item)
return chunk
def take_one_random(coll):
if len(coll) == 0:
return None
return coll.pop(_.random(0, len(coll)-1))
return coll.pop(_.random(0, len(coll) - 1))
def random_phone():
@ -62,7 +66,7 @@ def get_related_or_none(obj, attr):
rel = getattr(obj, attr)
except ObjectDoesNotExist:
rel = None
return rel
@ -75,20 +79,20 @@ def has_related(obj, attr):
def get_attr_or_none(klass, *args, attr=None, **kwargs):
object = get_or_none(klass, *args, **kwargs)
if object and attr and isinstance(attr, str):
return getattr(object, attr, None)
def model_fields(model, width=200):
fields = natsort.natsorted(model._meta.get_fields(), key=lambda f: f.name)
pprint([(
f.name,
'Relation? %s' % f.is_relation,
'Null? %s' % getattr(f, 'null', None),
'Blank? %s' % getattr(f, 'blank', None),
) for f in fields], width=width)
f.name,
'Relation? %s' % f.is_relation,
'Null? %s' % getattr(f, 'null', None),
'Blank? %s' % getattr(f, 'blank', None),
) for f in fields], width=width)
def lorem(words=None, sentences=5):
@ -101,58 +105,60 @@ def lorem(words=None, sentences=5):
'sit sollicitudin tempor tempus tincidunt tortor turpis ullamcorper ultricies ut varius vehicula vel velit vestibulum'
'vitae viverra volutpat vulputate'
), ' ')
return _.join(_.times(lambda i_: _.capitalize(_.join(_.sample(vocab, words or _.random(5, 30)), ' ')), sentences), '. ')
return _.join(_.times(lambda i_: _.capitalize(_.join(_.sample(vocab, words or _.random(5, 30)), ' ')), sentences),
'. ')
def decap(s):
if not isinstance(s, str):
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)
_.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)
def to_local_datetime(obj):
if not isinstance(obj, datetime.date):
raise TypeError('Date expected')
if not isinstance(obj, datetime.datetime):
obj = datetime.datetime.combine(obj, datetime.time.min)
if not getattr(obj, 'tzinfo', None):
obj = timezone.utc.localize(obj)
return obj
def morph(number, words):
CHOICES = (2, 0, 1, 1, 1, 2)
if 4 < number % 100 < 20:
choice = 2
else:
choice = CHOICES[number % 10 if number % 10 < 5 else 5]
return words[choice]
# # Example:
#
# words = ['яблоко', 'яблока', 'яблок']
@ -164,8 +170,8 @@ def morph(number, words):
class ASCIIFileSystemStorage(FileSystemStorage):
def get_valid_name(self, name):
symbols = (u"абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ",
u"abvgdeejzijklmnoprstufhzcss_y_euaABVGDEEJZIJKLMNOPRSTUFHZCSS_Y_EUA")
tr = {ord(a):ord(b) for a, b in zip(*symbols)}
u"abvgdeejzijklmnoprstufhzcss_y_euaABVGDEEJZIJKLMNOPRSTUFHZCSS_Y_EUA")
tr = {ord(a): ord(b) for a, b in zip(*symbols)}
name = name.translate(tr)
return super().get_valid_name(name)

@ -1,22 +1,15 @@
from django.conf import settings
from django.contrib import messages
from django.core.exceptions import PermissionDenied
import pydash as _;
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import HttpResponseForbidden, JsonResponse, HttpResponseRedirect, HttpResponse, Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import TemplateView, View
from pprint import pprint, pformat
import json
import logging
import pydash as _; _.map = _.map_; _.filter = _.filter_
from django.shortcuts import render
from django.views.generic import View
_.map = _.map_;
_.filter = _.filter_
from .mixins import BaseMixin
from archilance import util
from chat.models import Documents
from common.models import MainPage, PrintDocuments
from projects.models import Order
from users.models import User, ContractorResumeFiles
from common.models import MainPage
from users.models import User
from work_sell.models import Picture
@ -25,18 +18,18 @@ class HomeTemplateView(BaseMixin, View):
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
main_settings = MainPage.objects.get(pk=1)
context['main_settings'] = main_settings
return render(request, self.template_name, context)
class TestChatTemplateView(View):
template_name = 'chat_test.html'
def get(self, request, *args, **kwargs):
test = [54,55,56]
test = [54, 55, 56]
for pk in test:
picture = Picture.objects.get(pk=pk)
temp_file = ContentFile(picture.file.read())
@ -55,11 +48,10 @@ class TestView(BaseMixin, View):
def get(self, request, *args, **kwargs):
context = self.get_context_data(**kwargs)
context['foo'] = _.flatten_deep(User.contractor_objects.order_by('?')[:10].values_list('pk'))
return render(request, self.template_name, context)
# return redirect('projects:detail', pk=153)
# import code; code.interact(local=dict(globals(), **locals()))

@ -7,21 +7,23 @@ For more information on this file, see
https://docs.djangoproject.com/en/1.9/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
from django.template.base import Variable
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "archilance.settings.local")
application = get_wsgi_application()
# Patch template Variable to output empty string for None values:
old_resolve_lookup = Variable._resolve_lookup
def new_resolve_lookup(self, *args, **kwargs):
val = old_resolve_lookup(self, *args, **kwargs)
return '' if val == None else val
Variable._resolve_lookup = new_resolve_lookup

@ -1073,7 +1073,7 @@ footer:after {
}
.pagin nav ul li a {
background-color: none;
background-color: transparent;
color: black;
font-size: 20px;
line-height: 50px;
@ -1099,7 +1099,6 @@ footer:after {
.pagin nav ul li a:hover,
.pagin nav ul li a:active {
color: black;
color: white;
background-color: #2c2c2c;
border-color: #2c2c2c;
@ -2560,7 +2559,7 @@ input[type="checkbox"]:checked + span {
text-align: center;
margin: 0 0 0 0;
position: relative;
letter-spacing: 0px;
letter-spacing: 0;
}
.compareBlock p:before {
@ -2986,9 +2985,8 @@ input[type="checkbox"]:checked + span {
.compTable tr td:nth-child(6) ul li:nth-child(2):before {
height: 20px;
background: url('../img/listTable.png') no-repeat center;
background-size: cover;
background-position: 0 -17px;
background: url('../img/listTable.png') no-repeat 0 -17px;
}
.compTable tr td:nth-child(7) {
@ -3005,7 +3003,6 @@ input[type="checkbox"]:checked + span {
font-weight: bold;
font-family: Arial, Verdana, Helvetica, sans-serif;
position: relative;
position: relative;
width: 100%;
float: left;
}
@ -3839,7 +3836,7 @@ input[type="checkbox"]:checked + span {
border-top: 1px solid black;
border-bottom: 1px solid black;
-webkit-transform: scale(1.03);
-moz- transform: scale(1.03);
-moz-transform: scale(1.03);
transform: scale(1.03);
box-shadow: 0 0 10px rgba(0,0,0,0.7);
z-index: 999;
@ -3916,7 +3913,7 @@ input[type="checkbox"]:checked + span {
border-top: 1px solid black;
border-bottom: 1px solid black;
-webkit-transform: scale(1.03);
-moz- transform: scale(1.03);
-moz-transform: scale(1.03);
transform: scale(1.03);
box-shadow: 0 0 10px rgba(0,0,0,0.7);
z-index: 999;
@ -4286,7 +4283,6 @@ input[type="checkbox"]:checked + span {
#modal_form1 {
width: 600px;
height: 400px;
display: table;
position: fixed;
top: 50%;
left: 50%;
@ -4344,7 +4340,6 @@ input[type="checkbox"]:checked + span {
width: 300px;
height: 160px;
min-height: 160px;
display: table;
position: fixed;
right: 0;
top: 50%;
@ -5438,7 +5433,7 @@ input[type="radio"]:checked + span {
font-family: Arial, Verdana, Helvetica, sans-serif;
font-size: 16px;
font-weight: bold;
margin: 0px 0 -15 0;
margin: 0px 0 -15px 0;
}
.inp-edit {
@ -5536,7 +5531,7 @@ input[type="radio"]:checked + span {
.caret:after {
content: "\e253";
position: absolute;
font-family: 'Glyphicons Halflings';
font-family: 'Glyphicons Halflings', serif;
right: -6.5px;
top: -19px;
font-size: 12px;
@ -6080,7 +6075,7 @@ input[type="radio"]{
.welcomeMain{
line-height: 48px;
font-family: 'pfdintextcomppro-regular', sans-serif;
font-size: 43.5px;
font-size: 43px;
}
.menuUser > div{
padding: 10px 0;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

@ -180,10 +180,10 @@ $('[data-tooltip]').tooltip({
specSelects[2].select2(_.merge(specSelectOptions, {ajax: {url: specs.urlLevel3}}))
specSelects[3].select2(_.merge(specSelectOptions, {ajax: {url: specs.urlLevel4}}))
var specLevel1 = specs.specLevel1
var specLevel2 = specs.specLevel2
var specLevel3 = specs.specLevel3
var specLevel4 = specs.specLevel4
var specLevel1 = specs.specLevel1;
var specLevel2 = specs.specLevel2;
var specLevel3 = specs.specLevel3;
var specLevel4 = specs.specLevel4;
specSelects[0].select2('data', specLevel1 ? {id: specLevel1.id, text: specLevel1.name, origItem: specLevel1} : null)
specSelects[1].select2('data', specLevel2 ? {id: specLevel2.id, text: specLevel2.name, origItem: specLevel2} : null)

@ -1,4 +1,5 @@
from django.contrib import admin
from .models import Message, Notes, Documents, NewMessage
@ -11,11 +12,11 @@ class NotesAdmin(admin.ModelAdmin):
class DocumentsAdmin(admin.ModelAdmin):
list_display = ('sender', 'recipent', 'order','team')
list_display = ('sender', 'recipent', 'order', 'team')
class NewMessageAdmin(admin.ModelAdmin):
list_display = ('user', 'message_pk','message_recipent', 'message_order','message_team')
list_display = ('user', 'message_pk', 'message_recipent', 'message_order', 'message_team')
def message_pk(self, obj):
return obj.message.pk

@ -1,13 +1,11 @@
import html
from tornado import gen, web, websocket, escape, options
from tornado.ioloop import IOLoop
from tornado.httpserver import HTTPServer
from tornado.options import parse_command_line
from settings import settings, PORT, DATABASE_DSN
import psycopg2
import momoko
import json
from settings import settings, PORT, DATABASE_DSN
from tornado import gen, web, websocket, escape
from tornado.httpserver import HTTPServer
from tornado.ioloop import IOLoop
from tornado.options import parse_command_line
class BaseHandler(web.RequestHandler):
@ -91,8 +89,8 @@ class ChatHandler(websocket.WebSocketHandler):
insert_sql = "INSERT INTO chat_message (id,text,created, sender_id,recipent_id," \
" private_type,team_id, order_id,is_delete,is_new) " \
"VALUES (DEFAULT,'{0}',NOW(),{1},{2},{3},{4},{5},{6},{7}) RETURNING id".\
format(message, sender_id, recipent_id, private_type, team_value,order_value, is_delete, is_new)
"VALUES (DEFAULT,'{0}',NOW(),{1},{2},{3},{4},{5},{6},{7}) RETURNING id". \
format(message, sender_id, recipent_id, private_type, team_value, order_value, is_delete, is_new)
cursor_list = yield dict(cursor=self.db.execute(insert_sql))
cursor = cursor_list.get('cursor')
@ -102,19 +100,20 @@ class ChatHandler(websocket.WebSocketHandler):
team_ids = [t for t in team_ids_raw.rstrip(';').split(';')]
values_str = '';
for t in team_ids:
values_str +='(DEFAULT,{0},{1}),'.format(message_id,t)
values_str += '(DEFAULT,{0},{1}),'.format(message_id, t)
values_str = values_str.rstrip(',')
insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES{0}".\
insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES{0}". \
format(values_str)
else:
insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES(DEFAULT,{0},{1})".\
insert_new_messages = "INSERT INTO chat_newmessage (id,message_id, user_id) VALUES(DEFAULT,{0},{1})". \
format(message_id, recipent_id)
yield self.db.execute(insert_new_messages)
if docs_send_links:
is_send = 'true'
docs_send_ids = docs_send_links.rstrip(';').replace(';', ',')
update_sql_documents = "UPDATE chat_documents SET message_id={0},is_send={1} WHERE id IN({2})".format(message_id, is_send, docs_send_ids)
update_sql_documents = "UPDATE chat_documents SET message_id={0},is_send={1} WHERE id IN({2})".format(
message_id, is_send, docs_send_ids)
yield self.db.execute(update_sql_documents)
select_last_sql = "SELECT chat_message.id, chat_message.text, chat_message.created, chat_message.sender_id," \
@ -143,7 +142,7 @@ class ChatHandler(websocket.WebSocketHandler):
'sender_name': sender_name,
'answer_type': answer_type,
'docs_attach': docs_attach,
})
})
def check_origin(self, origin):
return True

@ -2,6 +2,7 @@ from rest_framework_filters import FilterSet, RelatedFilter, AllLookupsFilter
from .models import Message, Notes, Documents
#
# class DocumentsFilterSet(FilterSet):
# file = AllLookupsFilter()
@ -20,7 +21,7 @@ class MessageFilterSet(FilterSet):
id = AllLookupsFilter()
private_type = AllLookupsFilter()
text = AllLookupsFilter()
order = RelatedFilter('projects.filters.OrderFilterSet')
recipent = RelatedFilter('users.filters.UserFilterSet')
sender = RelatedFilter('users.filters.UserFilterSet')
@ -30,22 +31,20 @@ class MessageFilterSet(FilterSet):
model = Message
class DocumentFilterSet(FilterSet):
id = AllLookupsFilter()
# order = RelatedFilter('projects.filters.OrderFilterSet')
class Meta:
model = Documents
class NoteFilterSet(FilterSet):
created = AllLookupsFilter()
id = AllLookupsFilter()
text = AllLookupsFilter()
recipent = RelatedFilter('users.filters.UserFilterSet')
sender = RelatedFilter('users.filters.UserFilterSet')

@ -4,4 +4,3 @@ from django import forms
class ArticleForm(forms.Form):
title = forms.CharField()
pub_date = forms.DateField()

@ -8,7 +8,7 @@ from users.models import User, Team
class Message(models.Model):
text = models.TextField()
created = models.DateTimeField(default=timezone.now)
order = models.ForeignKey(Order, related_name='messages',null=True, blank=True)
order = models.ForeignKey(Order, related_name='messages', null=True, blank=True)
sender = models.ForeignKey(User, related_name='sender_messages')
recipent = models.ForeignKey(User, related_name='recipent_messages')
private_type = models.BooleanField(default=False)

@ -1,4 +1,5 @@
import json
from django.http import HttpResponse
MIMEANY = '*/*'

@ -1,8 +1,8 @@
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from .models import Message, Notes, Documents
from users.serializers import UserSerializer
from .models import Message, Notes, Documents
class DocumentsSerializer(ModelSerializer):
@ -44,7 +44,6 @@ class MessageSerializer(ModelSerializer):
documents = DocumentsSerializer(read_only=True, many=True)
text = serializers.SerializerMethodField()
class Meta:
model = Message
@ -62,14 +61,15 @@ class MessageSerializer(ModelSerializer):
def get_text(self, obj):
out = obj.text
documents = obj.documents.all()
if len(documents)>0:
documents_str = '<br>'.join(['Приложенный файл. скачать: <br><a target="_blank" href="/chat/download/' + doc.file.name + '">' + doc.file.name + '</a>' for doc in documents])
if len(documents) > 0:
documents_str = '<br>'.join([
'Приложенный файл. скачать: <br><a target="_blank" href="/chat/download/' + doc.file.name + '">' + doc.file.name + '</a>'
for doc in documents])
out += '<br><br>' + documents_str
return out
class NoteSerializer(ModelSerializer):
class Meta:
model = Notes

@ -13,7 +13,4 @@ settings = {
'server_traceback': True,
}
DATABASE_DSN = 'dbname=archilance user=postgres password=postgres host=localhost'

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

@ -1,5 +1,4 @@
import mimetypes
from django.core.urlresolvers import reverse
def serialize(instance, file_attr='file'):

@ -1,18 +1,19 @@
import json
from django.shortcuts import render
from wsgiref.util import FileWrapper
from django.conf import settings
from django.views.generic import View,CreateView
from django.http import HttpResponse, Http404
from django.db.models import Q
from django.contrib.auth.mixins import LoginRequiredMixin
from wsgiref.util import FileWrapper
from django.db.models import Q
from django.http import HttpResponse, Http404
from django.shortcuts import render
from django.views.generic import View, CreateView
from .response import JSONResponse, response_mimetype
from .utils import serialize
from .models import Message, Documents, NewMessage
from projects.models import Order, Project
from wallets.models import Transaction
from users.models import User, Team
from wallets.models import Transaction
from .models import Message, Documents
from .response import JSONResponse, response_mimetype
from .utils import serialize
class DocumentCreateView(CreateView):
@ -37,11 +38,11 @@ class ChatUserView(LoginRequiredMixin, View):
template_name = ''
def get(self, request, *args, **kwargs):
user_id = request.GET.get('user_id',None)
user_id = request.GET.get('user_id', None)
if request.user.is_customer():
customer_contacts = Message.objects.values_list('sender_id', 'recipent_id'). \
filter(Q(recipent_id=request.user.pk) | Q(sender_id=request.user.pk)).\
filter(Q(team_id=None)).\
filter(Q(recipent_id=request.user.pk) | Q(sender_id=request.user.pk)). \
filter(Q(team_id=None)). \
filter(is_delete=False).distinct()
users_ids = []
@ -57,16 +58,18 @@ class ChatUserView(LoginRequiredMixin, View):
contacts_users = User.objects.filter(pk__in=users_ids)
archive_projects = request.user.customer_projects.select_related('order').exclude(state='active')
chat_messages = Message.objects.filter(Q(sender=request.user.pk) | Q(recipent=request.user.pk))
orders = request.user.customer_projects.select_related('order').filter(state='active').exclude(order__contractor__isnull=True, order__team__isnull=True)
orders = request.user.customer_projects.select_related('order').filter(state='active').exclude(
order__contractor__isnull=True, order__team__isnull=True)
order_ids = [order.order.pk for order in orders]
transaction = Transaction.objects.get_or_create(customer=request.user, type='reservation', complete=False)
contacts_users_count = request.user.new_messages.filter(message__sender__in=users_ids,
message__order__isnull=True,
message__team__isnull=True
).count()
message__order__isnull=True,
message__team__isnull=True
).count()
orders_ms_count = request.user.new_messages.filter(message__order__in=order_ids, message__team__isnull=True).count()
orders_ms_count = request.user.new_messages.filter(message__order__in=order_ids,
message__team__isnull=True).count()
self.template_name = 'chat_customer.html'
return render(request, self.template_name, {'contacts_users': contacts_users,
'chat_messages': chat_messages,
@ -81,17 +84,19 @@ class ChatUserView(LoginRequiredMixin, View):
team_ids = []
if request.user.is_owner_team():
team_ids.append(request.user.team.pk)
# team_orders = request.user.team.orders.all()
# team_orders = request.user.team.orders.all()
# # teams = Team.objects.filter(contractors__id=request.user.pk).all()
# else:
teams = Team.objects.filter(Q(contractors__id=request.user.pk) | Q(owner=request.user.pk)).all()
team_orders = Order.objects.filter(team_id__in=[team.pk for team in teams]).all()
orders = Order.objects.filter(Q(contractor=request.user) | Q(team_id__in=team_ids)).filter(project__state='active')
archive_orders = Order.objects.filter(Q(contractor=request.user) | Q(team_id__in=team_ids)).exclude(project__state='active')
orders = Order.objects.filter(Q(contractor=request.user) | Q(team_id__in=team_ids)).filter(
project__state='active')
archive_orders = Order.objects.filter(Q(contractor=request.user) | Q(team_id__in=team_ids)).exclude(
project__state='active')
contractor_contacts = Message.objects.values_list('sender_id', 'recipent_id').filter(
Q(recipent_id=request.user.pk) | Q(sender_id=request.user.pk)).filter(Q(team_id=None)).\
Q(recipent_id=request.user.pk) | Q(sender_id=request.user.pk)).filter(Q(team_id=None)). \
filter(is_delete=False).distinct()
users_ids = []
for msg in contractor_contacts:
@ -105,15 +110,16 @@ class ChatUserView(LoginRequiredMixin, View):
contacts_users = User.objects.filter(pk__in=users_ids)
contacts_users_count = request.user.new_messages.filter(message__sender__in=users_ids,
message__order__isnull=True,
message__team__isnull=True
).count()
message__order__isnull=True,
message__team__isnull=True
).count()
chat_messages = Message.objects.filter(Q(sender=request.user.pk) | Q(recipent=request.user.pk)).order_by(
'created')
your_teams = Team.objects.filter(Q(contractors__id=request.user.pk) | Q(owner=request.user))
orders_ms_count = request.user.new_messages.filter(message__order__in=orders, message__team__isnull=True).count()
orders_ms_count = request.user.new_messages.filter(message__order__in=orders,
message__team__isnull=True).count()
teams_ms_count = request.user.new_messages.filter(message__team__in=your_teams).count()
self.template_name = 'chat_contractor.html'
@ -146,7 +152,7 @@ def project_delete(request):
if request.is_ajax():
project_id = request.POST.get('project_id')
try:
project = Project.objects.get(pk=project_id,customer=request.user)
project = Project.objects.get(pk=project_id, customer=request.user)
except Documents.DoesNotExist:
project = None
if project:
@ -160,7 +166,7 @@ def project_delete(request):
raise Http404
def download_file(request,file_name):
def download_file(request, file_name):
try:
document = Documents.objects.get(file=file_name)
except Documents.DoesNotExist:

@ -1,7 +1,10 @@
import pydash as _;
from django import forms
from django.contrib import admin
from mptt.admin import MPTTModelAdmin
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
from .models import (
LiveImageUpload,
@ -21,17 +24,17 @@ class LocationAdmin(MPTTModelAdmin):
class MainPageAdminForm(forms.ModelForm):
class Meta:
fields = '__all__'
class Media:
js = (
'admin.js',
'lib/ckeditor/ckeditor.js',
'lib/ckeditor/adapters/jquery.js',
)
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name in ('contractor_text', 'customer_text'):
attrs = self.fields[field_name].widget.attrs
attrs['class'] = _.join(_.compact((attrs.get('class'), '-ckeditor')), ' ')
@ -45,17 +48,17 @@ class MainPageAdmin(admin.ModelAdmin):
class TooltipAdminForm(forms.ModelForm):
class Meta:
fields = '__all__'
class Media:
js = (
'admin.js',
'lib/ckeditor/ckeditor.js',
'lib/ckeditor/adapters/jquery.js',
)
def __init__(self, *args, **kwargs):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field_name in ('text', 'example'):
attrs = self.fields[field_name].widget.attrs
attrs['class'] = _.join(_.compact((attrs.get('class'), '-ckeditor')), ' ')

@ -12,7 +12,7 @@ class LocationFilterSet(FilterSet):
rght = AllLookupsFilter()
tree_id = AllLookupsFilter()
type = AllLookupsFilter()
children = RelatedFilter('common.filters.LocationFilterSet')
parent = RelatedFilter('common.filters.LocationFilterSet')
@ -24,6 +24,6 @@ class ContentTypeFilterSet(FilterSet):
app_label = AllLookupsFilter()
id = AllLookupsFilter()
model = AllLookupsFilter()
class Meta:
model = ContentType

@ -1,10 +1,14 @@
import pydash as _;
from django import forms
from registration.forms import RegistrationFormTermsOfService
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
from .models import PrintOrder, PrintDocuments
from captcha.fields import ReCaptchaField
class PrintOrderForm(forms.ModelForm):
files = forms.ModelMultipleChoiceField(
queryset=PrintDocuments.objects.none(),
@ -37,18 +41,18 @@ class CustomRegistrationForm(RegistrationFormTermsOfService):
('customer', 'Заказчик'),
('contractor', 'Исполнитель'),
)
user_type = forms.ChoiceField(choices=USER_TYPES)
captcha = ReCaptchaField()
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
if self.request.GET.get('type') == 'customer':
self.fields['user_type'].initial = 'customer'
elif self.request.GET.get('type') == 'contractor':
self.fields['user_type'].initial = 'contractor'
attrs = self.fields['user_type'].widget.attrs
attrs['class'] = _.join(_.compact((attrs.get('class'), 'selectpicker3')), ' ')

@ -1,11 +1,12 @@
import datetime
from django.utils.timezone import now
from users.models import User
from ratings.models import HistoryRating
from users.models import User
class SetLastVisitMiddleware(object):
def process_response(self, request, response):
if hasattr(request, 'user'):
if request.user.is_authenticated():
@ -14,7 +15,6 @@ class SetLastVisitMiddleware(object):
class SetRatingToUserEveryDay(object):
def process_response(self, request, response):
if hasattr(request, 'user'):
today_date = datetime.datetime.now().date()
@ -27,4 +27,3 @@ class SetRatingToUserEveryDay(object):
hs_new.description = 'Балл за вход на сайт'
hs_new.save()
return response

@ -44,7 +44,7 @@ class Settings(models.Model):
document_send_time_remove = models.IntegerField(default=14)
recalculation_spec_time = models.TimeField()
recalculation_rating_time = models.TimeField()
noreply_email = models.CharField(max_length=50,default='noreply@proekton.com')
noreply_email = models.CharField(max_length=50, default='noreply@proekton.com')
def __str__(self):
return 'Настройки сайта'
@ -93,16 +93,16 @@ class Tooltip(models.Model):
('top', 'Сверху'),
('bottom', 'Снизу'),
)
name = models.CharField(max_length=255, unique=True)
text = models.TextField(blank=True)
example = models.TextField(blank=True)
position = models.CharField(max_length=255, choices=POSITIONS, default='top')
class Meta:
verbose_name = 'Подсказка'
verbose_name_plural = 'Подсказки'
def __str__(self):
return self.name
@ -110,10 +110,10 @@ class Tooltip(models.Model):
class LiveImageUpload(models.Model):
file = models.ImageField(upload_to='live-image-upload/')
created_at = models.DateTimeField(default=timezone.now)
class Meta:
verbose_name = 'Живая загрузка изображений'
verbose_name_plural = 'Живая загрузка изображений'
def __str__(self):
return self.file.url

@ -7,7 +7,7 @@ from .models import Location
class NestedLocationSerializer(ModelSerializer):
class Meta:
model = Location
fields = (
'children',
'id',
@ -24,7 +24,7 @@ class NestedLocationSerializer(ModelSerializer):
class LocationSerializer(ModelSerializer):
children = NestedLocationSerializer(many=True)
parent = NestedLocationSerializer()
class Meta:
model = Location

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

@ -12,7 +12,8 @@ app_name = 'common'
urlpatterns = [
urls.url(r'^printdocument/create/$', PrintDocumentCreate.as_view(), name='create'),
urls.url(r'^printorder/(?P<pk>\d+)/$', PrintOrderDetailView.as_view(), name='print-order-detail'),
urls.url(r'^live-image-upload/create/$', LiveImageUploadCreateView.as_view(), name='live-image-upload-create'),
urls.url(r'^live-image-upload/(?P<pk>\d+)/delete/$', LiveImageUploadDeleteView.as_view(), name='live-image-upload-delete'),
urls.url(r'^live-image-upload/(?P<pk>\d+)/delete/$', LiveImageUploadDeleteView.as_view(),
name='live-image-upload-delete'),
]

@ -1,8 +1,7 @@
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import Group
from django.core.exceptions import PermissionDenied
from django.core.mail import send_mail, EmailMultiAlternatives
from django.core.mail import EmailMultiAlternatives
from django.core.urlresolvers import reverse, reverse_lazy
from django.http import JsonResponse
from django.shortcuts import render, redirect
@ -10,15 +9,13 @@ from django.template.loader import get_template, render_to_string
from django.views.generic import View, DetailView
from registration.backends.default.views import RegistrationView
from sorl.thumbnail import get_thumbnail
import json
import jsonschema
from .forms import PrintOrderForm, CustomRegistrationForm
from .models import PrintDocuments, PrintOrder, Settings, LiveImageUpload
from archilance import util
from archilance.mixins import BaseMixin
from common.mixins import NoCsrfMixin
from users.models import ContractorResume
from .forms import PrintOrderForm, CustomRegistrationForm
from .models import PrintDocuments, PrintOrder, Settings, LiveImageUpload
class PrintOrderDetailView(DetailView):
@ -62,7 +59,6 @@ class PrintDocumentCreate(BaseMixin, View):
'files': link_files,
}
settings = Settings.objects.all().first()
subject, from_email, to = 'Заявка на распечатку', settings.noreply_email, settings.document_send_email
text_content = render_to_string('document_email.txt', ctx_dict)
@ -74,7 +70,7 @@ class PrintDocumentCreate(BaseMixin, View):
msg.send()
messages.info(request, 'Заявка на распечатку принята')
return redirect('common:print-order-detail',pk=print_order.pk)
return redirect('common:print-order-detail', pk=print_order.pk)
# import code; code.interact(local=dict(globals(), **locals()))
else:
context = self.get_context_data(**kwargs)
@ -82,16 +78,15 @@ class PrintDocumentCreate(BaseMixin, View):
return render(request, self.template_name, context)
class LiveImageUploadCreateView(NoCsrfMixin, LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
image = request.FILES.get('image')
if not image:
return JsonResponse({'files': [{'error': 'No image provided'}]})
live_img = LiveImageUpload.objects.create(file=image)
return JsonResponse({
'id': live_img.pk,
'name': live_img.file.name,
@ -107,7 +102,7 @@ class LiveImageUploadCreateView(NoCsrfMixin, LoginRequiredMixin, View):
class LiveImageUploadDeleteView(NoCsrfMixin, LoginRequiredMixin, View):
def post(self, request, *args, **kwargs):
live_img = util.get_or_none(LiveImageUpload, pk=kwargs.get('pk'))
if live_img:
live_img.file.delete()
live_img.delete()
@ -120,38 +115,37 @@ class CustomRegistrationView(RegistrationView):
form_class = CustomRegistrationForm
template_name = 'registration/registration_form.html'
success_url = reverse_lazy('registration_complete')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['request'] = self.request
return kwargs
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
if self.request.GET.get('type') in ('customer', 'contractor'):
context['hide_user_type'] = True
return context
def register(self, form):
user = super().register(form)
if form.cleaned_data.get('user_type') == 'customer':
group = Group.objects.get(name='Заказчики')
elif form.cleaned_data.get('user_type') == 'contractor':
group = Group.objects.get(name='Исполнители')
else:
group = None
if group:
user.groups.add(group)
user.contractor_resume = ContractorResume.objects.create(text='')
user.save()
return user
return user
# import code; code.interact(local=dict(globals(), **locals()))

@ -25,17 +25,17 @@ class BuildingClassficationAdmin(MPTTModelAdmin):
class ProjectAdminForm(forms.ModelForm):
files = forms.ModelMultipleChoiceField(queryset=ProjectFile.objects.none(), required=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.fields['files'].queryset = self.instance.files
class ProjectAdmin(admin.ModelAdmin):
readonly_fields = ('pk',)
list_display = ('name','pk','customer','state')
list_display = ('name', 'pk', 'customer', 'state')
form = ProjectAdminForm
@ -44,7 +44,7 @@ class OrderAdmin(admin.ModelAdmin):
class StageAdmin(admin.ModelAdmin):
list_display = ('name','status','pos','order','is_paid',)
list_display = ('name', 'status', 'pos', 'order', 'is_paid',)
admin.site.register(Answer)

@ -6,5 +6,4 @@ class ProjectsConfig(AppConfig):
name = 'projects'
def ready(self):
import projects.signals
pass

@ -16,7 +16,7 @@ from .models import (
class BuildingClassficationFilterSet(FilterSet):
id = AllLookupsFilter()
name = AllLookupsFilter()
class Meta:
model = BuildingClassfication
@ -24,7 +24,7 @@ class BuildingClassficationFilterSet(FilterSet):
class ConstructionTypeFilterSet(FilterSet):
id = AllLookupsFilter()
name = AllLookupsFilter()
class Meta:
model = ConstructionType
@ -44,13 +44,13 @@ class ProjectFilterSet(FilterSet):
term_type = AllLookupsFilter()
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')
class Meta:
model = Project
@ -66,16 +66,16 @@ class AnswerFilterSet(FilterSet):
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
@ -85,7 +85,7 @@ class OrderFilterSet(FilterSet):
id = AllLookupsFilter()
secure = AllLookupsFilter()
status = AllLookupsFilter()
contractor = RelatedFilter('users.filters.UserFilterSet')
project = RelatedFilter('projects.filters.ProjectFilterSet')
team = RelatedFilter('users.filters.TeamFilterSet')
@ -105,7 +105,7 @@ class StageFilterSet(FilterSet):
status = AllLookupsFilter()
term = AllLookupsFilter()
term_type = AllLookupsFilter()
order = RelatedFilter('projects.filters.OrderFilterSet')
class Meta:
@ -115,22 +115,23 @@ class StageFilterSet(FilterSet):
class RealtyFilterSet(FilterSet):
id = AllLookupsFilter()
name = AllLookupsFilter()
building_classification = RelatedFilter('projects.filters.BuildingClassficationFilterSet')
construction_type = RelatedFilter('projects.filters.ConstructionTypeFilterSet')
location = RelatedFilter('common.filters.LocationFilterSet')
user = RelatedFilter('users.filters.UserFilterSet')
class Meta:
model = Realty
class PortfolioPhotoFilterSet(FilterSet):
id = AllLookupsFilter()
portfolio = RelatedFilter('projects.filters.PortfolioFilterSet')
# img = ???
class Meta:
model = PortfolioPhoto
@ -142,9 +143,9 @@ class PortfolioFilterSet(FilterSet):
name = AllLookupsFilter()
term = AllLookupsFilter()
term_type = AllLookupsFilter()
photos = RelatedFilter('projects.filters.PortfolioPhotoFilterSet')
user = RelatedFilter('users.filters.UserFilterSet')
class Meta:
model = Portfolio

@ -1,10 +1,13 @@
import itertools
import pydash as _;
from django import forms
from django.db.models import Q
from django.forms.models import inlineformset_factory
from mptt.forms import TreeNodeChoiceField
from pprint import pprint, pformat
import itertools
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
from .models import (
Project, ProjectFile, Portfolio, Answer,
@ -14,47 +17,47 @@ from .models import (
from archilance import util
from common.models import Location, LiveImageUpload
from specializations.models import Specialization
from users.models import User, Team
from users.models import Team
class ProjectFilterForm(forms.ModelForm):
PROJECT_ORDER_CHOICES = ( # "Упорядочить по"...
PROJECT_ORDER_CHOICES = ( # "Упорядочить по"...
('name', 'названию'),
('budget', 'цене'),
('created', 'дате размещения'),
('views', 'просмотрам'),
)
order_by = forms.ChoiceField(required=False, choices=PROJECT_ORDER_CHOICES)
last_order_by = forms.ChoiceField(required=False, choices=PROJECT_ORDER_CHOICES)
reverse_order = forms.BooleanField(required=False)
keywords = forms.CharField(required=False, max_length=255)
class Meta:
model = Project
fields = (
'cro',
'work_type',
'realty',
'specialization',
)
widgets = {
'work_type': forms.Select(attrs={'class': 'selectpicker6 -project-work-type-select-field'}),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['work_type'].choices = tuple(itertools.chain((('',''),), self.fields['work_type'].choices))
self.fields['work_type'].choices = tuple(itertools.chain((('', ''),), self.fields['work_type'].choices))
self.fields['work_type'].required = False
self.fields['work_type'].initial = ''
self.fields['specialization'].required = False
self.fields['specialization'].queryset = Specialization.objects.root_nodes()[0].get_descendants()
# self.fields['specialization'].queryset = Specialization.objects # Migrate with this enabled
@ -62,45 +65,46 @@ class ProjectFilterForm(forms.ModelForm):
class ProjectFilterRealtyForm(forms.ModelForm):
class Meta:
model = Realty
fields = (
'building_classification',
'construction_type',
'location',
)
widgets = {
'building_classification': forms.Select(attrs={'class': 'selectpicker'}),
'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['building_classification'].empty_label = ''
self.fields['building_classification'].required = False
self.fields['construction_type'].empty_label = ''
self.fields['construction_type'].required = False
self.fields['location'].queryset = Location.objects.root_nodes()[0].get_descendants()
self.fields['building_classification'].queryset = BuildingClassfication.objects.root_nodes()[0].get_descendants()
self.fields['building_classification'].queryset = BuildingClassfication.objects.root_nodes()[
0].get_descendants()
# self.fields['location'].queryset = Location.objects # Migrate with this enabled
class CustomerProjectEditForm(forms.ModelForm):
# Define a form field manually for a reverse model vield:
files = forms.ModelMultipleChoiceField(
queryset=ProjectFile.objects.none(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
class Meta:
model = Project
fields = (
'budget',
'budget_by_agreement',
@ -116,38 +120,38 @@ class CustomerProjectEditForm(forms.ModelForm):
'text',
'work_type',
)
widgets = {
'currency': forms.Select(attrs={'class': 'selectpicker2 valul'}),
'realty': forms.Select(attrs={'class': 'selectpicker'}),
'term_type': forms.Select(attrs={'class': 'selectpicker'}),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
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
@ -164,27 +168,28 @@ class RealtyForm(forms.ModelForm):
class Meta:
model = Realty
fields = (
'building_classification',
'construction_type',
'location',
'name',
)
widgets = {
'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
if not self.data.get('extra_fields'):
self.fields['name'].required = False
self.fields['location'].queryset = Location.objects.root_nodes()[0].get_descendants()
self.fields['building_classification'].queryset = BuildingClassfication.objects.root_nodes()[0].get_descendants()
self.fields['building_classification'].queryset = BuildingClassfication.objects.root_nodes()[
0].get_descendants()
# self.fields['location'].queryset = Location.objects # Migrate with this enabled
@ -200,7 +205,7 @@ class PortfolioForm(forms.ModelForm):
class Meta:
model = Portfolio
fields = '__all__'
widgets = {
'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
'currency': forms.Select(attrs={'class': 'selectpicker'}),
@ -211,13 +216,13 @@ class PortfolioForm(forms.ModelForm):
class PortfolioEditForm(forms.ModelForm):
# Define a form field manually for a reverse model vield:
photos = forms.ModelMultipleChoiceField(
queryset=PortfolioPhoto.objects.none(),
widget=forms.CheckboxSelectMultiple,
required=False,
)
live_images = forms.ModelMultipleChoiceField(
queryset=LiveImageUpload.objects.all(),
widget=forms.CheckboxSelectMultiple,
@ -229,15 +234,15 @@ class PortfolioEditForm(forms.ModelForm):
widget=forms.Select(attrs={'class': 'selectpicker'}),
level_indicator='',
)
class Meta:
model = Portfolio
fields = (
'building_classification',
'construction_type',
'specialization',
'budget',
'currency',
'description',
@ -247,27 +252,27 @@ class PortfolioEditForm(forms.ModelForm):
'work_type',
'worksell',
)
widgets = {
'construction_type': forms.Select(attrs={'class': 'selectpicker'}),
'currency': forms.Select(attrs={'class': 'selectpicker'}),
'term_type': forms.Select(attrs={'class': 'selectpicker'}),
'work_type': forms.Select(attrs={'class': 'selectpicker -project-work-type-select-field'}),
}
def __init__(self, *args, **kwargs):
# self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['photos'].queryset = self.instance.photos.all()
class ProjectAnswerForm(forms.ModelForm):
text = forms.CharField(widget=forms.Textarea)
class Meta:
model = Answer
fields = (
'budget',
'currency',
@ -276,61 +281,62 @@ class ProjectAnswerForm(forms.ModelForm):
'term',
'term_type',
)
widgets = {
'currency': forms.Select(attrs={'class': 'selectpicker'}),
'term_type': forms.Select(attrs={'class': 'selectpicker'}),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
self.project = kwargs.pop('project')
self.answer_as_team = kwargs.pop('answer_as_team', None)
super().__init__(*args, **kwargs)
if self.answer_as_team:
self.fields['secure_deal_only'].label = 'Работаем только по безопасной сделке'
contractor = self.request.user
members = contractor.team.contractors.all() if util.has_related(contractor, 'team') else ()
# portfolios = []
# portfolios.extend(contractor.portfolios.all())
#
# for c in members:
# portfolios.extend(c.portfolios.all())
portfolios = Portfolio.objects.filter(user__pk__in=tuple(m.pk for m in itertools.chain((contractor,), members)))
portfolios = Portfolio.objects.filter(
user__pk__in=tuple(m.pk for m in itertools.chain((contractor,), members)))
self.fields['portfolios'].queryset = portfolios
else:
self.fields['secure_deal_only'].label = 'Работаю только по безопасной сделке'
self.fields['portfolios'].queryset = self.request.user.portfolios.all()
if self.project and self.project.price_and_term_required:
self.fields['budget'].required = True
self.fields['currency'].required = True
self.fields['term'].required = True
self.fields['term_type'].required = True
def clean(self):
if self.answer_as_team:
try:
self.request.user.team
except Team.DoesNotExist:
raise forms.ValidationError('У исполнителя отсутствует команда')
return super().clean()
class ProjectAnswerMessageForm(forms.ModelForm):
class Meta:
model = AnswerMessage
fields = (
'text',
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
@ -339,7 +345,7 @@ class ProjectAnswerMessageForm(forms.ModelForm):
class StageForm(forms.ModelForm):
class Meta:
model = Stage
fields = (
# 'cost',
# 'cost_type',
@ -355,11 +361,11 @@ PortfolioPhotoFormSet = inlineformset_factory(Portfolio, PortfolioPhoto, fields=
class CustomerProjectTrashForm(forms.Form):
pk = forms.ModelChoiceField(queryset=Project.objects.none())
def __init__(self, *args, **kwargs):
self.req = kwargs.pop('req')
super().__init__(*args, **kwargs)
self.fields['pk'].queryset = self.req.user.customer_projects.filter(state='active')
@ -369,52 +375,51 @@ class ContractorPortfolioTrashForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['pk'].queryset = self.request.user.portfolios.all()
class CustomerProjectRestoreForm(forms.Form):
pk = forms.ModelChoiceField(queryset=Project.objects.none())
def __init__(self, *args, **kwargs):
self.req = kwargs.pop('req')
super().__init__(*args, **kwargs)
self.fields['pk'].queryset = self.req.user.customer_projects.filter(state='trashed')
class CustomerProjectDeleteForm(forms.Form):
pk = forms.ModelChoiceField(queryset=Project.objects.none())
def __init__(self, *args, **kwargs):
self.req = kwargs.pop('req')
super().__init__(*args, **kwargs)
self.fields['pk'].queryset = self.req.user.customer_projects.filter(Q(state='active') | Q(state='trashed'))
class ProjectWorkTypeSuggestionForm(forms.ModelForm):
class Meta:
model = ProjectWorkTypeSuggestion
fields = (
'name',
'commentary',
'email',
'username',
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super().__init__(*args, **kwargs)
# for bfield in self:
# attrs = bfield.field.widget.attrs
# attrs['class'] = _.join(_.compact((attrs.get('class'), '-error -error-%s' % bfield.html_name)), ' ')
if self.request and self.request.user.is_authenticated():
self['username'].field.initial = self.request.user.username
self['email'].field.initial = self.request.user.email
# import code; code.interact(local=dict(globals(), **locals()))

@ -0,0 +1,48 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-10-14 22:49+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: templates/_trash/project_form.html:46
#: templates/contractor_portfolio_edit.html:46
#: templates/customer_project_create.html:62
#: templates/customer_project_edit.html:72 templates/project_filter.html:21
msgid "project_stage0"
msgstr "Тип работы"
#: templates/contractor_portfolio_edit.html:59
#: templates/customer_project_create.html:89
#: templates/customer_project_edit.html:99 templates/project_filter.html:35
msgid "project_stage1"
msgstr "Стадия проекта"
#: templates/contractor_portfolio_edit.html:64
#: templates/customer_project_create.html:94
#: templates/customer_project_edit.html:104 templates/project_filter.html:39
msgid "project_stage2"
msgstr "Раздел"
#: templates/contractor_portfolio_edit.html:69
#: templates/customer_project_create.html:99
#: templates/customer_project_edit.html:109 templates/project_filter.html:43
msgid "project_stage3"
msgstr "Подраздел"
#: templates/contractor_portfolio_edit.html:74
#: templates/customer_project_create.html:104
#: templates/customer_project_edit.html:114 templates/project_filter.html:47
msgid "project_stage4"
msgstr "Подраздел ( доп. )"

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-14 20:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('projects', '0046_merge'),
]
operations = [
migrations.AlterModelOptions(
name='buildingclassfication',
options={'ordering': ['order'], 'verbose_name': 'Тип здания', 'verbose_name_plural': 'Типы зданий'},
),
migrations.AlterModelOptions(
name='constructiontype',
options={'ordering': ['order'], 'verbose_name': 'Вид строительства', 'verbose_name_plural': 'Виды строительства'},
),
migrations.AlterModelManagers(
name='buildingclassfication',
managers=[
],
),
migrations.AddField(
model_name='buildingclassfication',
name='order',
field=models.PositiveSmallIntegerField(default=0, null=True),
),
migrations.AddField(
model_name='constructiontype',
name='order',
field=models.PositiveSmallIntegerField(default=0, null=True),
),
]

@ -1,17 +1,19 @@
import pydash as _;
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
from django.core.exceptions import ValidationError
from django.utils import timezone
from hitcount.models import HitCountMixin
from mptt.managers import TreeManager
from mptt.models import TreeForeignKey, MPTTModel
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
from users.models import User, Team
from specializations.models import Specialization
CURRENCIES = (
('rur', 'RUR'),
('usd', 'USD'),
@ -36,22 +38,31 @@ TERM_TYPE_MORPHS = {
class BuildingClassfication(MPTTModel):
name = models.CharField(max_length=255)
parent = TreeForeignKey('self', blank=True, null=True, related_name='children', db_index=True)
order = models.PositiveSmallIntegerField(default=0, null=True)
objects = TreeManager()
def __str__(self):
return self.name
class MPTTMeta:
order_insertion_by = ['order']
class Meta:
ordering = ['order']
verbose_name = 'Тип здания'
verbose_name_plural = 'Типы зданий'
class ConstructionType(models.Model):
name = models.CharField(max_length=255)
order = models.PositiveSmallIntegerField(default=0, null=True)
def __str__(self):
return self.name
class Meta:
ordering = ['order']
verbose_name = 'Вид строительства'
verbose_name_plural = 'Виды строительства'
@ -61,7 +72,7 @@ class Realty(models.Model):
construction_type = models.ForeignKey(ConstructionType, related_name='realties')
location = TreeForeignKey('common.Location', related_name='realties', null=True, blank=True)
name = models.CharField(max_length=255)
user = models.ForeignKey(User, related_name='realties') # Do we actually need this field?
user = models.ForeignKey(User, related_name='realties') # Do we actually need this field?
def __str__(self):
return self.name
@ -77,18 +88,18 @@ class Project(models.Model, HitCountMixin):
(2, 'Техническое сопровождение'),
# (3, 'Устранение замечаний в проекте'),
)
DEAL_TYPES = (
('secure_deal', 'Безопасная сделка'), # "Безопасная сделка (с резервированием бюджета)"
('direct_payment', 'Прямая оплата'), # "Прямая оплата Исполнителю на его кошелек/счет"
('secure_deal', 'Безопасная сделка'), # "Безопасная сделка (с резервированием бюджета)"
('direct_payment', 'Прямая оплата'), # "Прямая оплата Исполнителю на его кошелек/счет"
)
STATES = (
('active', 'Активный'),
('trashed', 'В корзине'),
('deleted', 'Удален'),
)
budget = models.DecimalField(max_digits=10, decimal_places=0)
budget_by_agreement = models.BooleanField(default=False)
created = models.DateTimeField(default=timezone.now)
@ -105,18 +116,18 @@ class Project(models.Model, HitCountMixin):
term_type = models.CharField(max_length=20, choices=TERM_TYPES, default='hour')
text = models.TextField(blank=True)
work_type = models.IntegerField(default=1, choices=WORK_TYPES)
def __str__(self):
return self.name
class Meta:
verbose_name = 'Проект'
verbose_name_plural = 'Проекты'
ordering = ('-created',)
def secure_deal(self):
return self.deal_type == 'secure_deal'
def get_team_answers(self):
return _.filter(self.answers.all(), lambda a: isinstance(a.author, Team))
@ -124,11 +135,11 @@ class Project(models.Model, HitCountMixin):
class ProjectFile(models.Model):
file = models.FileField(upload_to='projects/project_files/')
project = models.ForeignKey(Project, related_name='files', blank=True, null=True)
class Meta:
verbose_name = 'Файл проекта'
verbose_name_plural = 'Файлы проектов'
def __str__(self):
return self.file and self.file.url or self.pk
@ -137,27 +148,29 @@ class Answer(models.Model):
budget = models.DecimalField(max_digits=10, decimal_places=0, blank=True, null=True)
created = models.DateTimeField(default=timezone.now)
currency = models.CharField(max_length=5, choices=CURRENCIES, blank=True, null=True)
portfolios = models.ManyToManyField('Portfolio', related_name ='answers', blank=True)
portfolios = models.ManyToManyField('Portfolio', related_name='answers', blank=True)
project = models.ForeignKey(Project, related_name='answers')
secure_deal_only = models.BooleanField(default=False)
term = models.IntegerField(blank=True, null=True)
term_type = models.CharField(max_length=10, choices=TERM_TYPES, 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'))
content_type = models.ForeignKey(ContentType,
limit_choices_to=Q(app_label='users', model='user') | Q(app_label='users',
model='team'))
object_id = models.IntegerField()
author = GenericForeignKey('content_type', 'object_id')
def get_first_message(self):
message = self.messages.first()
if message:
return message.text
def __str__(self):
return "{author}'s answer ({id})".format(author=type(self.author).__name__, id=self.pk)
class Meta:
verbose_name = 'Отклик на проект'
verbose_name_plural = 'Отклики на проекты'
@ -169,27 +182,27 @@ class AnswerMessage(models.Model):
#
# customer: `message.is_sender_customer and message.answer.project.customer`
# contractor/team: `not message2.is_sender_customer and message2.contractor_or_team`
answer = models.ForeignKey(Answer, related_name='messages')
created = models.DateTimeField(default=timezone.now)
is_sender_customer = models.BooleanField(default=False)
seen_by_contractor = models.BooleanField(default=False)
seen_by_customer = models.BooleanField(default=False)
text = models.TextField()
content_type = models.ForeignKey(
ContentType,
limit_choices_to=Q(app_label='users', model='user') | Q(app_label='users', model='team'),
null=True,
blank=True,
)
object_id = models.IntegerField(null=True, blank=True)
contractor_or_team = GenericForeignKey('content_type', 'object_id')
def __str__(self):
return str(self.pk)
class Meta:
ordering = ['created']
verbose_name = 'Отклики на проекты -- переписка'
@ -200,11 +213,11 @@ 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/')
class Meta:
verbose_name = 'Файл для отклика'
verbose_name_plural = 'Файлы для откликов'
def __str__(self):
return self.file and self.file.url or self.pk
@ -215,17 +228,18 @@ class Order(models.Model):
('process', 'В процессе'),
('completed', 'Завершен'),
)
contractor = models.ForeignKey(User, null=True, blank=True, related_name='orders') # Related name should've been "contractor_orders"
contractor = models.ForeignKey(User, null=True, blank=True,
related_name='orders') # Related name should've been "contractor_orders"
created = models.DateTimeField(default=timezone.now)
project = models.OneToOneField(Project, related_name='order')
secure = models.BooleanField(default=False)
status = models.CharField(max_length=30, choices=STATUSES, default='created')
team = models.ForeignKey(Team, null=True, blank=True, related_name='orders')
def __str__(self):
return self.project.name
class Meta:
verbose_name = 'Заказ'
verbose_name_plural = 'Заказы'
@ -238,6 +252,7 @@ class Order(models.Model):
else:
return None
class Arbitration(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
@ -252,7 +267,7 @@ class Arbitration(models.Model):
verbose_name_plural = 'Арбитраж'
unique_together = (
('user', 'order'),
('user', 'order'),
)
@ -264,7 +279,6 @@ STATUSES = (
('completed', 'Завершен'),
)
from .validators import validate_term
class Stage(models.Model):
cost = models.DecimalField(max_digits=10, decimal_places=0)
@ -304,8 +318,9 @@ class Stage(models.Model):
class Candidate(models.Model):
answer = models.ForeignKey(Answer, related_name='candidates') # TODO: Swap to "OneToOneField"
project = models.ForeignKey(Project, related_name='candidates') # TODO: Remove this redundant field at all (we've got "candidate.answer.project")
answer = models.ForeignKey(Answer, related_name='candidates') # TODO: Swap to "OneToOneField"
project = models.ForeignKey(Project,
related_name='candidates') # TODO: Remove this redundant field at all (we've got "candidate.answer.project")
status = models.BooleanField(default=False)
position = models.PositiveIntegerField(default=0)
@ -356,17 +371,17 @@ class Portfolio(models.Model):
def get_cover(self):
photo = self.photos.first()
return photo and photo.img # WTF? We could at leat return a URL, not an object
return photo and photo.img # WTF? We could at leat return a URL, not an object
class PortfolioPhoto(models.Model):
img = models.ImageField(upload_to='projects/portfolio/')
portfolio = models.ForeignKey(Portfolio, related_name='photos')
class Meta:
verbose_name = 'Фото портфолио'
verbose_name_plural = 'Фото портфолио'
def __str__(self):
return self.img and self.img.url or str(self.img)
@ -377,10 +392,10 @@ class ProjectWorkTypeSuggestion(models.Model):
processed = models.BooleanField(default=False, verbose_name='Рассмотрено?')
username = models.CharField(max_length=255, blank=True, verbose_name='Имя пользователя')
email = models.EmailField(verbose_name='Email')
class Meta:
verbose_name = 'Предложение типов работ (проектов)'
verbose_name_plural = 'Предложения типов работ (проектов)'
def __str__(self):
return self.name

@ -1,20 +1,21 @@
from rest_framework import serializers
from generic_relations.relations import GenericRelatedField
from rest_framework.serializers import ModelSerializer, ImageField, FileField, SerializerMethodField, PrimaryKeyRelatedField, ReadOnlyField
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer, ImageField, FileField, PrimaryKeyRelatedField
from .models import Project, Realty, BuildingClassfication, ConstructionType, Order, Stage, Portfolio, PortfolioPhoto, Answer, AnswerFile
from common.serializers import LocationSerializer, ContentTypeSerializer
from specializations.serializers import SpecializationSerializer
from users.models import User, Team
from users.serializers import UserSerializer, TeamSerializer
from .models import Project, Realty, BuildingClassfication, ConstructionType, Order, Stage, Portfolio, PortfolioPhoto, \
Answer, AnswerFile
class AnswerFileSerializer(ModelSerializer):
file = FileField()
class Meta:
model = AnswerFile
fields = (
'file',
'id',
@ -26,10 +27,10 @@ class AnswerFileSerializer(ModelSerializer):
class PortfolioPhotoSerializer(ModelSerializer):
img = ImageField()
portfolio_id = PrimaryKeyRelatedField(read_only=True, source='portfolio')
class Meta:
model = PortfolioPhoto
fields = (
'id',
'img',
@ -40,7 +41,7 @@ class PortfolioPhotoSerializer(ModelSerializer):
class BuildingClassficationSerializer(ModelSerializer):
class Meta:
model = BuildingClassfication
fields = (
'id',
'name',
@ -50,7 +51,7 @@ class BuildingClassficationSerializer(ModelSerializer):
class ConstructionTypeSerializer(ModelSerializer):
class Meta:
model = ConstructionType
fields = (
'id',
'name',
@ -62,14 +63,14 @@ class RealtySerializer(ModelSerializer):
construction_type = ConstructionTypeSerializer()
location = LocationSerializer()
user = UserSerializer()
class Meta:
model = Realty
fields = (
'id',
'name',
'building_classification',
'construction_type',
'location',
@ -78,16 +79,16 @@ class RealtySerializer(ModelSerializer):
class StageSerializer(ModelSerializer):
term = serializers.DateField(format="%d.%m.%Y", input_formats=['%d.%m.%Y',])
term = serializers.DateField(format="%d.%m.%Y", input_formats=['%d.%m.%Y', ])
def validate(self, data):
if 'pos' in data and data['pos'] > 1:
pos = data['pos'] -1
pos = data['pos'] - 1
stage_last = Stage.objects.filter(order=data['order'], pos=pos)
if stage_last:
stage_last = stage_last[0]
if stage_last.term > data['term']:
raise serializers.ValidationError({'term':'Дата не должна быть меньше даты предыдущео этапа'})
raise serializers.ValidationError({'term': 'Дата не должна быть меньше даты предыдущео этапа'})
return data
class Meta:
@ -116,7 +117,7 @@ class OrderSerializer_(ModelSerializer):
stages = StageSerializer(many=True)
team = TeamSerializer()
project_id = PrimaryKeyRelatedField(read_only=True, source='project')
class Meta:
model = Order
@ -125,7 +126,7 @@ class OrderSerializer_(ModelSerializer):
'id',
'secure',
'status',
'contractor',
'project_id',
'stages',
@ -136,18 +137,18 @@ 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',
@ -161,8 +162,8 @@ class AnswerSerializer_(ModelSerializer):
'secure_deal_only',
'term',
'term_type',
'author', # Generic related field
'author', # Generic related field
'content_type',
'files',
)
@ -171,13 +172,13 @@ class AnswerSerializer_(ModelSerializer):
class ProjectSerializer(ModelSerializer):
answers = AnswerSerializer_(many=True)
customer = UserSerializer()
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:
model = Project
fields = (
'answers',
'budget',
@ -253,10 +254,10 @@ class PortfolioSerializer(ModelSerializer):
photos = PortfolioPhotoSerializer(many=True)
specialization = SpecializationSerializer()
user = UserSerializer()
class Meta:
model = Portfolio
fields = (
# 'answers',
'building_classification',
@ -265,7 +266,7 @@ class PortfolioSerializer(ModelSerializer):
'photos',
'specialization',
'user',
'budget',
'currency',
'description',
@ -282,15 +283,15 @@ class AnswerSerializer(ModelSerializer):
files = AnswerFileSerializer(many=True)
portfolios = PortfolioSerializer(many=True)
project = ProjectSerializer()
author = GenericRelatedField({
User: UserSerializer(),
Team: TeamSerializer()
})
class Meta:
model = Answer
fields = (
'budget',
'created',
@ -302,12 +303,12 @@ class AnswerSerializer(ModelSerializer):
'secure_deal_only',
'term',
'term_type',
'author', # Generic related field
'author', # Generic related field
'content_type',
'files',
'portfolios',
'project',
# 'candidates',
)

@ -1,8 +1,8 @@
from django.core.signals import request_finished
from django.db.models.signals import post_save
from django.utils import timezone
from django.dispatch import receiver
from .models import Stage
from django.utils import timezone
from .models import Stage
@receiver(post_save, sender=Stage)

@ -1,279 +1,279 @@
{% extends 'partials/base.html' %}
{% block content %}
<div class="col-lg-12 allProjects">
<p class="titleScore">Новый заказ</p>
</div>
<div class="col-lg-12 allProjects">
<p class="titleScore">Новый заказ</p>
</div>
<form method="post">
{% csrf_token %}
{{ form_project.errors }}
{{ form_realty.errors }}
<div class="chatBlock new-rass new-rass2 disTab">
{{ project_form.errors }}
<div class="col-lg-9">
<p class="new-pp new-pp3">Формирование заказа</p>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название заказа</p>
<input type="text" class="box-sizing" name="{{ project_form.name.name }}"/>
{{ project_form.name.errors }}
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Подробно опишите задание</p>
<textarea name="{{ project_form.text.name }}" id="text-new"></textarea>
</div>
</div>
<div class="col-lg-3 wrChat1">
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div class="col-lg-12 documentsChat">
{# <form action="" method="post">#}
{# <div class="upload">#}
{# <input type="file" name="upload"/>#}
{# <p>+ добавить файл (до 100 файлов)</p>#}
{# </div>#}
{# <input type="submit" value="Submit" />#}
{# </form>#}
<ul class="list-new-new">
<li>
Архитерурное 2.jpg
<span>7мб</span>
<div></div>
</li>
</ul>
</div>
<p class="type-work">Тип работы:</p>
<div class="mail-block type-work-inset">
<div class="inset-mb">
<label><input type="radio" value="1" name="{{ project_form.type_work.name }}"><span></span></label>
<p>Проектирование</p>
</div>
<form method="post">
{% csrf_token %}
{{ form_project.errors }}
{{ form_realty.errors }}
<div class="chatBlock new-rass new-rass2 disTab">
{{ project_form.errors }}
<div class="col-lg-9">
<p class="new-pp new-pp3">Формирование заказа</p>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название заказа</p>
<input type="text" class="box-sizing" name="{{ project_form.name.name }}"/>
{{ project_form.name.errors }}
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Подробно опишите задание</p>
<textarea name="{{ project_form.text.name }}" id="text-new"></textarea>
</div>
</div>
<div class="col-lg-3 wrChat1">
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div class="col-lg-12 documentsChat">
{# <form action="" method="post">#}
{# <div class="upload">#}
{# <input type="file" name="upload"/>#}
{# <p>+ добавить файл (до 100 файлов)</p>#}
{# </div>#}
{# <input type="submit" value="Submit" />#}
{# </form>#}
{# <ul class="list-new-new">#}
{# <li>#}
{# Архитерурное 2.jpg#}
{# <span>7мб</span>#}
{# <div></div>#}
{# </li>#}
{# </ul>#}
</div>
<p class="type-work">{% trans 'project_stage0' %}:</p>
<div class="mail-block type-work-inset">
<div class="inset-mb">
<label><input type="radio" value="1" name="{{ project_form.type_work.name }}"><span></span></label>
<p>Проектирование</p>
</div>
<div class="inset-mb">
<label><input type="radio" value="2" name="{{ project_form.type_work.name }}"><span></span></label>
<p>Техническое сопровождение</p>
</div>
</div>
<div class="textAreaBlock2 box-sizing disTab">
<a href="javascriptt:void(0)" class="new-link new-lw">+ Добавить раздел</a>
</div>
</div>
<div class="inset-mb">
<label><input type="radio" value="2" name="{{ project_form.type_work.name }}"><span></span></label>
<p>Техническое сопровождение</p>
</div>
</div>
<div class="textAreaBlock2 box-sizing disTab">
<a href="javascriptt:void(0)" class="new-link new-lw">+ Добавить раздел</a>
</div>
</div>
</div>
<div class="col-lg-12 new-filter">
<div class="filter clearfix">
<div class="titleF1 disTab">
<div class="col-lg-3">Специализация проекта:</div>
<div class="col-lg-3"></div>
<div class="col-lg-3"></div>
<div class="col-lg-3"></div>
</div>
<div class="col-lg-12 new-filter">
<div class="filter clearfix">
<div class="titleF1 disTab">
<div class="col-lg-3">Специализация проекта:</div>
<div class="col-lg-3"></div>
<div class="col-lg-3"></div>
<div class="col-lg-3"></div>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
<div class="polsF1 disTab">
<div class="col-lg-3">
<select id="specialization1" name="{{ project_form.specialization.name }}">
</select>
<select id="specialization1" name="{{ project_form.specialization.name }}">
</select>
{# {{ project_form.specialization }}#}
</div>
<div class="col-lg-3">
<select class="selectpicker">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
{# <select class="selectpicker">#}
{# <option>Mustard</option>#}
{# <option>Ketchup</option>#}
{# <option>Relish</option>#}
{# </select>#}
</div>
<div class="col-lg-3">
{# <select class="selectpicker">#}
{# <option>Mustard</option>#}
{# <option>Ketchup</option>#}
{# <option>Relish</option>#}
{# </select>#}
</div>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-4">Бюджет</div>
<div class="col-lg-8"></div>
</div>
<div class="searchF1 polsF1 polsFF">
<div class="col-lg-4">
<input type="text" name="{{ project_form.price.name }}" class="box-sizing surr">
<select class="selectpicker2 valul">
<option>&#36;</option>
<option>&#36;</option>
<option>&#36;</option>
</select>
</div>
<div class="col-lg-3">
<select class="selectpicker">
<option>За проект</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-5 dog-new">
<label><input type="checkbox" name="dogovor"><span></span></label>
<p>или по договоренности</p>
</div>
</div>
<div class="searchF1 polsF1 polsFF make-new">
<label><input type="checkbox" name="dogovor"><span></span></label>
<p>Сделать для исполнителей обязательным для заполнения поля цена и срок</p>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Способ оплаты</div>
</div>
<div class="searchF1 polsF1 polsFF radio-afer">
<div class="col-lg-6">
<label><input type="radio" name="afer"><span></span></label>
<p class="text-afer">
Безопасная сделка (с резервированием бюджета)
</p>
<p class="des-afer">
Текст
</p>
</div>
<div class="col-lg-6">
<label><input type="radio" name="afer"><span></span></label>
<p class="text-afer">
Прямая оплата Исполнителю на его кошелек/счет
</p>
<p class="des-afer">
Текст
</p>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенный поиск</p>
<button class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта:</div>
<div class="col-lg-3">Наименование:</div>
<div class="col-lg-3">Классификация здания:</div>
<div class="col-lg-3">Вид строительства:</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select class="selectpicker" >
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
<input type="text" class="box-sizing surr surr2" name="{{ realty_form.name.name }}">
</div>
<div class="col-lg-3">
<select class="selectpicker" name="{{ realty_form.building_classification.name }}">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
<select class="selectpicker" name="{{ realty_form.type_construction.name }}">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение:</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select class="selectpicker" name="{{ realty_form.country.name }}">
<option>Страна</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
<select class="selectpicker" {{ realty_form.city.name }}>
<option>Город</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-6 make-new">
<label><input type="checkbox" name="dogovor"><span></span></label>
<p>Требуется допуск СРО</p>
</div>
</div>
{# {{ project_form.specialization }}#}
</div>
<div class="col-lg-3">
<select class="selectpicker">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
{# <select class="selectpicker">#}
{# <option>Mustard</option>#}
{# <option>Ketchup</option>#}
{# <option>Relish</option>#}
{# </select>#}
</div>
<div class="col-lg-3">
{# <select class="selectpicker">#}
{# <option>Mustard</option>#}
{# <option>Ketchup</option>#}
{# <option>Relish</option>#}
{# </select>#}
</div>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-4">Бюджет</div>
<div class="col-lg-8"></div>
</div>
<div class="searchF1 polsF1 polsFF">
<div class="col-lg-4">
<input type="text" name="{{ project_form.price.name }}" class="box-sizing surr">
<select class="selectpicker2 valul">
<option>&#36;</option>
<option>&#36;</option>
<option>&#36;</option>
</select>
</div>
<div class="col-lg-3">
<select class="selectpicker">
<option>За проект</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-5 dog-new">
<label><input type="checkbox" name="dogovor"><span></span></label>
<p>или по договоренности</p>
</div>
</div>
<div class="searchF1 polsF1 polsFF make-new">
<label><input type="checkbox" name="dogovor"><span></span></label>
<p>Сделать для исполнителей обязательным для заполнения поля цена и срок</p>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Способ оплаты</div>
</div>
<div class="searchF1 polsF1 polsFF radio-afer">
<div class="col-lg-6">
<label><input type="radio" name="afer"><span></span></label>
<p class="text-afer">
Безопасная сделка (с резервированием бюджета)
</p>
<p class="des-afer">
Текст
</p>
</div>
<div class="col-lg-6">
<label><input type="radio" name="afer"><span></span></label>
<p class="text-afer">
Прямая оплата Исполнителю на его кошелек/счет
</p>
<p class="des-afer">
Текст
</p>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенный поиск</p>
<button class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта:</div>
<div class="col-lg-3">Наименование:</div>
<div class="col-lg-3">Классификация здания:</div>
<div class="col-lg-3">Вид строительства:</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select class="selectpicker">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
<input type="text" class="box-sizing surr surr2" name="{{ realty_form.name.name }}">
</div>
<div class="col-lg-3">
<select class="selectpicker" name="{{ realty_form.building_classification.name }}">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
<select class="selectpicker" name="{{ realty_form.type_construction.name }}">
<option>Mustard</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение:</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select class="selectpicker" name="{{ realty_form.country.name }}">
<option>Страна</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-3">
<select class="selectpicker" {{ realty_form.city.name }}>
<option>Город</option>
<option>Ketchup</option>
<option>Relish</option>
</select>
</div>
<div class="col-lg-6 make-new">
<label><input type="checkbox" name="dogovor"><span></span></label>
<p>Требуется допуск СРО</p>
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input style="border-radius: 40px;
<div class="searchF1 polsF1 polsFF links-filter">
<input style="border-radius: 40px;
font-family: 'pfdintextcomppro-regular', sans-serif;color: black;font-size: 16px;padding: 17px 33px 17px 33px;
float: left;margin: 0 15px 48px 15px;border: 1px solid #DFDFDF;text-transform: uppercase;
letter-spacing: 2px;color: #42B476;border: 1px solid #42B476" type="submit" value="Разместить проект" />
<a href="javascript:void(0)">Сохранить</a>
letter-spacing: 2px;color: #42B476;border: 1px solid #42B476" type="submit" value="Разместить проект"/>
<a href="javascript:void(0)">Сохранить</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>
</form>
{% endblock %}
{% block js_block %}
<script type="text/javascript">
$(function () {
<script type="text/javascript">
$(function () {
function updateSelectData() {
function updateSelectData(){
}
}
$.ajax({
url: '/api/specializations?parent=1',
type: 'GET',
dataType: 'json',
success: function (json) {
$.ajax({
url: '/api/specializations?parent=1',
type: 'GET',
dataType: 'json',
success: function (json) {
console.log(json.results);
var out = ''
$.each(json.results, function (i, v) {
$('#specialization1')
.append($("<option></option>")
.attr("value", v.name)
.text(v.name));
});
console.log(json.results);
var out = ''
$.each(json.results, function (i, v) {
$('#specialization1')
.append($("<option></option>")
.attr("value", v.name)
.text(v.name));
});
$("#specialization1").addClass("selectpicker");
$('.selectpicker').selectpicker({
style: 'btn-info',
size: 4,
width: '237px'
});
}
});
$("#specialization1").addClass("selectpicker");
$('.selectpicker').selectpicker({
style: 'btn-info',
size: 4,
width: '237px'
});
}
});
$("#specialization1").on("change",function(){
$("#specialization1").on("change", function () {
});
});
</script>
});
});
</script>
{% endblock %}

@ -1,18 +1,18 @@
<form method="post" action="">{% csrf_token %}
{{ form.as_p }}
<fieldset>
<legend>Photos</legend>
{{ portfolio_photo_form.management_form }}
{{ portfolio_photo_form.non_form_errors }}
{% for form in portfolio_photo_form %}
{{ form.id }}
<div class="inline {{ portfolio_photo_form.prefix }}">
{{ form.img.errors }}
{{ form.img.label_tag }}
{{ form.img }}
</div>
{% endfor %}
</fieldset>
<input type="submit" value="Add portfolio" class="submit" />
<fieldset>
<legend>Photos</legend>
{{ portfolio_photo_form.management_form }}
{{ portfolio_photo_form.non_form_errors }}
{% for form in portfolio_photo_form %}
{{ form.id }}
<div class="inline {{ portfolio_photo_form.prefix }}">
{{ form.img.errors }}
{{ form.img.label_tag }}
{{ form.img }}
</div>
{% endfor %}
</fieldset>
<input type="submit" value="Add portfolio" class="submit"/>
</form>

@ -3,7 +3,7 @@
<h2>Мои проекты</h2>
{% for proj in request.user.customer_projects.all %}
<h3>{{ proj }}</h3>
<h5>{{ proj.order }}</h5>
{% endfor %}
{% for proj in request.user.customer_projects.all %}
<h3>{{ proj }}</h3>
<h5>{{ proj.order }}</h5>
{% endfor %}

@ -4,155 +4,159 @@
{% load i18n %}
{% 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="titleBlockComparison disTab">
<div class="triangle1"></div>
<p>{{ object }}</p>
<table class="compTable" id="compTable">
<thead>
<tr>
<th></th>
<th>Кандидат</th>
<th>Цена</th>
<th>Срок</th>
<th>Описание</th>
<th>Рейтинги/отзывы</th>
<th>Безопасные сделки</th>
<th>Решение</th>
</tr>
</thead>
<tbody>
{% for cand in object.candidates.all %}
<tr style="cursor:move;" class="items[]_{{ cand.pk }}" data-class="items[]_{{ cand.pk }}">
<td>{{ cand.position }}</td>
<td>
{% if cand.answer.author|class_name == 'User' %}
{% firstof cand.answer.author.get_full_name.strip cand.answer.author.username %}
{% elif cand.answer.author|class_name == 'Team' %}
{% firstof cand.answer.author.name.strip cand.answer.author.username %}
{% endif %}
</td>
<td>{{ cand.answer.budget }} <i class="{% fa_currency_classes cand.answer.currency %}"></i></td>
<td>
{% if cand.answer.term_type == 'project' %}
За проект
{% elif cand.answer.term and cand.answer.term_type %}
{% morph cand.answer.term TERM_TYPE_MORPHS|get:cand.answer.term_type %}
{% endif %}
</td>
<td>
<span class="glyphicon glyphicon-info-sign" aria-hidden="true" data-tooltip data-placement="right" title="{{ cand.answer.get_first_message }}"></span>
</td>
<td>
<ul>
<li><span>{{ cand.answer.author.rating }}</span></li>
<li>
<span>+0</span> 0 <small> - 0</small>
</li>
{% if cand.answer.author.cro %}
<li>
СРО
</li>
{% endif %}
</ul>
</td>
<td>
<span>0</span><br>
{% if cand.answer.secure_deal_only %}
Готов работать по безопасной сделке
{% endif %}
</td>
<td>
<div class="tableButtons disTab">
<form action="{% url 'projects:customer-offer-order' answer_id=cand.answer.pk project_id=cand.project.pk %}" method="POST" novalidate>
{% csrf_token %}
<a href="#" onclick="$(this).closest('form').submit(); return false">
<div class="btnTab btnTab1" title="Предложить проект"></div>
</a>
</form>
<a href="{% url 'chat:chat-user' %}?user_id={{ cand.answer.author.pk }}">
<div class="btnTab btnTab2" title="Перейти в чат"></div>
</a>
<form method="POST" action="{% url 'projects:delete-candidate' cand.pk %}">
{% csrf_token %}
<input class="btnTab btnTab3" type="submit" value="" title="Удалить из сравнения">
</form>
<a href="{% url 'users:contractor-profile' cand.answer.author.pk %}#open-contact">
<div class="btnTab btnTab4" title="Показать контакты"></div>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include 'partials/footer.html' %}
</div>
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12">
<p class="titleScore">Сравнение кандидатов по проекту</p>
</div>
<div class="titleBlockComparison disTab">
<div class="triangle1"></div>
<p>{{ object }}</p>
<table class="compTable" id="compTable">
<thead>
<tr>
<th></th>
<th>Кандидат</th>
<th>Цена</th>
<th>Срок</th>
<th>Описание</th>
<th>Рейтинги/отзывы</th>
<th>Безопасные сделки</th>
<th>Решение</th>
</tr>
</thead>
<tbody>
{% for cand in object.candidates.all %}
<tr style="cursor:move;" class="items[]_{{ cand.pk }}" data-class="items[]_{{ cand.pk }}">
<td>{{ cand.position }}</td>
<td>
{% if cand.answer.author|class_name == 'User' %}
{% firstof cand.answer.author.get_full_name.strip cand.answer.author.username %}
{% elif cand.answer.author|class_name == 'Team' %}
{% firstof cand.answer.author.name.strip cand.answer.author.username %}
{% endif %}
</td>
<td>{{ cand.answer.budget }} <i class="{% fa_currency_classes cand.answer.currency %}"></i></td>
<td>
{% if cand.answer.term_type == 'project' %}
За проект
{% elif cand.answer.term and cand.answer.term_type %}
{% morph cand.answer.term TERM_TYPE_MORPHS|get:cand.answer.term_type %}
{% endif %}
</td>
<td>
<span class="glyphicon glyphicon-info-sign" aria-hidden="true" data-tooltip data-placement="right"
title="{{ cand.answer.get_first_message }}"></span>
</td>
<td>
<ul>
<li><span>{{ cand.answer.author.rating }}</span></li>
<li>
<span>+0</span> 0
<small> - 0</small>
</li>
{% if cand.answer.author.cro %}
<li>
СРО
</li>
{% endif %}
</ul>
</td>
<td>
<span>0</span><br>
{% if cand.answer.secure_deal_only %}
Готов работать по безопасной сделке
{% endif %}
</td>
<td>
<div class="tableButtons disTab">
<form
action="{% url 'projects:customer-offer-order' answer_id=cand.answer.pk project_id=cand.project.pk %}"
method="POST" novalidate>
{% csrf_token %}
<a href="#" onclick="$(this).closest('form').submit(); return false">
<div class="btnTab btnTab1" title="Предложить проект"></div>
</a>
</form>
<a href="{% url 'chat:chat-user' %}?user_id={{ cand.answer.author.pk }}">
<div class="btnTab btnTab2" title="Перейти в чат"></div>
</a>
<form method="POST" action="{% url 'projects:delete-candidate' cand.pk %}">
{% csrf_token %}
<input class="btnTab btnTab3" type="submit" value="" title="Удалить из сравнения">
</form>
<a href="{% url 'users:contractor-profile' cand.answer.author.pk %}#open-contact">
<div class="btnTab btnTab4" title="Показать контакты"></div>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}
{% block js_block %}
<script>
$(function () {
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
return ui;
};
$("#compTable tbody").sortable({
forcePlaceholderSize: true,
forceHelperSize: true,
items: 'tr',
update: function () {
var serial = $('#compTable tbody').sortable('serialize', {key: 'items[]', attribute: 'data-class'});
console.log(serial);
$.ajax({
url: '/projects/candidate/comparison/sort/',
method: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
},
data: serial,
dataType: 'json',
success: function (json) {
console.log(json);
},
error: function (jqXHR, e) {
console.log(jqXHR);
console.log(e);
}
});
},
helper: fixHelper,
}).disableSelection();
;
<script>
$(function () {
var fixHelper = function (e, ui) {
ui.children().each(function () {
$(this).width($(this).width());
});
</script>
return ui;
};
$("#compTable tbody").sortable({
forcePlaceholderSize: true,
forceHelperSize: true,
items: 'tr',
update: function () {
var serial = $('#compTable tbody').sortable('serialize', {key: 'items[]', attribute: 'data-class'});
console.log(serial);
$.ajax({
url: '/projects/candidate/comparison/sort/',
method: 'POST',
beforeSend: function (xhr) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
},
data: serial,
dataType: 'json',
success: function (json) {
console.log(json);
},
error: function (jqXHR, e) {
console.log(jqXHR);
console.log(e);
}
});
},
helper: fixHelper,
}).disableSelection();
;
});
</script>
{% endblock %}

@ -1,177 +1,184 @@
{% extends 'partials/base.html' %}
{% load thumbnail %}
{% load i18n %}
{% 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>
<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' %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">Изменение портфолио</p>
</div>
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">Изменение портфолио</p>
</div>
<form method="post" enctype="multipart/form-data" id="worksell-add-form" class="-spec-work-type-combo-container">
<form method="post" enctype="multipart/form-data" id="worksell-add-form" class="-spec-work-type-combo-container">
{% csrf_token %}
{{ form.errors }}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название <span style="color: red">{{ form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ form.name.html_name }}" value="{{ form.name.value }}">
<input type="hidden" name="{{ form.contractor.html_name }}" value="{{ form.contractor.value }}" />
<p>Название <span style="color: red">{{ form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ form.name.html_name }}" value="{{ form.name.value }}">
<input type="hidden" name="{{ form.contractor.html_name }}" value="{{ form.contractor.value }}"/>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Описание<span style="color: red">{{ form.description.errors.as_text }}</span></p>
<textarea name="{{ form.description.html_name }}" id="text-new">{{ form.description.value }}</textarea>
<p>Описание<span style="color: red">{{ form.description.errors.as_text }}</span></p>
<textarea name="{{ form.description.html_name }}" id="text-new">{{ form.description.value }}</textarea>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>{% trans 'project_stage0' %} <span style="color: red">{{ form.work_type.errors.as_text }}</span></p>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Тип работы <span style="color: red">{{ form.work_type.errors.as_text }}</span></p>
</div>
<div class="col-lg-3">
<div class="polsF1 disTab" style="padding:0;">
{{ form.work_type }}
</div>
<div class="polsF1 disTab" style="padding:0;">
{{ form.work_type }}
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Стадия проекта <span style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type='hidden' class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">Стадия проекта</span></div>
<input type='hidden' class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage1' %} <span
style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type='hidden' class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Раздел</div>
<input type='hidden' class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">{% trans 'project_stage2' %}</span></div>
<input type='hidden' class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Подраздел</div>
<input type='hidden' class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage3' %}</div>
<input type='hidden' class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage4' %}</div>
<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" class="-chosen-spec-id" name="{{ form.specialization.html_name }}"
value="{{ form.specialization.value }}">
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
<div class="col-lg-4">
{{ form.currency}}
</div>
<p>Бюджет{{ form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
</div>
<div class="col-lg-4">
{{ form.currency }}
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Срок выполнения{{ form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.term.html_name }}" value="{{ form.term.value }}">
</div>
<div class="col-lg-4">
{{ form.term_type }}
</div>
<p>Срок выполнения{{ form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.term.html_name }}" value="{{ form.term.value }}">
</div>
<div class="col-lg-4">
{{ form.term_type }}
</div>
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
</div>
</div>
<div class="polsF1 polsF2 disTab">
<p>Вид строительства</p>
{{ form.construction_type}}
<p>Вид строительства</p>
{{ form.construction_type }}
</div>
<div class="polsF1 polsF2 disTab">
<p>Классификация здания</p>
{{ form.building_classification}}
<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>
<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>
</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="textAreaBlock2 text-nn box-sizing disTab">
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input type="submit" value="Сохранить" class="btn-submit-link add_file_to_port">
<a href="{% url 'users:contractor-profile' request.user.pk %}" class="btn-submit-link add_file_to_port">Отмена</a>
<input type="submit" value="Сохранить" class="btn-submit-link add_file_to_port">
<a href="{% url 'users:contractor-profile' request.user.pk %}"
class="btn-submit-link add_file_to_port">Отмена</a>
</div>
</form>
{% include 'partials/footer.html' %}
</div>
</form>
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}

@ -1,346 +1,410 @@
{% extends 'partials/base.html' %}
{% load i18n %}
{% block head_css %}
<style>
.-error, .errorlist {color: red}
</style>
<style>
.-error, .errorlist {
color: red
}
</style>
{% endblock %}
{% block content %}
{% include 'partials/modals/project_work_type_suggestion.html' %}
{% include 'partials/header.html' %}
{% include 'partials/modals/project_work_type_suggestion.html' %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">Новый заказ</p>
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">Новый заказ</p>
</div>
<form action="{% url 'projects:customer-project-create' %}" method="POST" enctype="multipart/form-data" novalidate
class="-spec-work-type-combo-container">
{% 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>
{% if form.non_field_errors %}
<div class="new-pp3" style="color: red; margin-top: 70px">{{ form.non_field_errors }}</div>
{% endif %}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p class="titleResF1">Название заказа <span data-tooltip data-placement="{% tooltip_placement pk=4 %}"
title="{% tooltip pk=4 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ form.name.html_name }}" value="{{ form.name.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p class="titleResF1">Подробно опишите задание <span data-tooltip
data-placement="{% tooltip_placement pk=5 %}"
title="{% tooltip pk=5 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.text.errors.as_text }}</span></p>
<textarea name="{{ form.text.html_name }}" id="text-new"
style="margin-top:0;">{{ form.text.value }}</textarea>
</div>
</div>
<div class="col-lg-3 wrChat1">
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div id="fileUploadContainer" class="col-lg-12 documentsChat">
<div class="upload">
<p id="fileUploadAddBtn" style="margin: 0">+ добавить файл (до 100 файлов)</p>
</div>
<ul class="list-new-new">
<li class="file-upload-widget" style="display: none">
<input type="file" name="new_files" class="file-upload-input"
style="position: absolute; top: -1000px; left: -1000px">
<p class="file-upload-label"></p>
<div class="file-upload-remove-btn"></div>
</li>
</ul>
</div>
<p class="type-work">{% trans 'project_stage0' %} <span data-tooltip
data-placement="{% tooltip_placement pk=6 %}"
title="{% tooltip pk=6 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.work_type.errors.as_text }}</span></p>
<div class="mail-block type-work-inset -project-work-type-radios-container">
{% for id, text in form.work_type.field.choices %}
<div class="inset-mb">
<label>
<input
type="radio"
value="{{ id }}"
{% if form.work_type.value|int == id %}checked{% endif %}
name="{{ form.work_type.html_name }}">
<span></span>
</label>
<p>{{ text }}</p>
</div>
{% endfor %}
</div>
<div class="textAreaBlock2 box-sizing disTab">
<a href="#" onclick="return false" data-toggle="modal" data-target="#projectWorkTypeSuggestionModal"
class="new-link new-lw">+ Добавить раздел</a>
</div>
</div>
</div>
<div class="col-lg-12 new-filter">
<div class="filter clearfix">
<div class="polsF1 disTab">
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage1' %} <span data-tooltip
data-placement="{% tooltip_placement pk=7 %}"
title="{% tooltip pk=7 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">{% trans 'project_stage2' %}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage3' %}</div>
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage4' %}</div>
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<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 data-tooltip data-placement="{% tooltip_placement pk=8 %}"
title="{% tooltip pk=8 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.budget.errors.as_text }}</span></div>
<!--<div class="col-lg-8"></div>-->
</div>
<div class="searchF1 polsF1 polsFF">
<div class="col-lg-4">
<input type="text" class="box-sizing surr" name="{{ form.budget.html_name }}"
value="{{ form.budget.value }}">
{{ form.currency }}
</div>
<div class="col-lg-3">
{{ form.term_type }}
</div>
<div class="col-lg-5 dog-new">
<label>
<input
type="checkbox"
{% if form.budget_by_agreement.value %}checked{% endif %}
name="{{ form.budget_by_agreement.html_name }}">
<span></span>
</label>
<p>или по договоренности <span data-tooltip data-placement="{% tooltip_placement pk=9 %}"
title="{% tooltip pk=9 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span></p>
</div>
</div>
<div class="searchF1 polsF1 polsFF make-new">
<label>
<input
type="checkbox"
{% if form.price_and_term_required.value %}checked{% endif %}
name="{{ form.price_and_term_required.html_name }}">
<span></span>
</label>
<p>Сделать для исполнителей обязательным для заполнения поля цена и срок</p>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Способ оплаты <span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.deal_type.errors.as_text }}</span>
</div>
</div>
<div class="searchF1 polsF1 polsFF radio-afer">
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'secure_deal' %}checked{% endif %}
value="secure_deal">
<span></span>
</label>
<p class="text-afer">
Безопасная сделка (с резервированием бюджета)
</p>
<p class="des-afer">
{% tooltip pk=10 as tooltip10 %}{{ tooltip10|linebreaksbr }}
</p>
</div>
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'direct_payment' %}checked{% endif %}
value="direct_payment">
<span></span>
</label>
<p class="text-afer">
Прямая оплата Исполнителю на его кошелек/счет
</p>
<p class="des-afer">
{% tooltip pk=11 as tooltip11 %}{{ tooltip11|linebreaksbr }}
</p>
</div>
</div>
<form action="{% url 'projects:customer-project-create' %}" method="POST" enctype="multipart/form-data" novalidate class="-spec-work-type-combo-container">
{% 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>
{% if form.non_field_errors %}
<div class="new-pp3" style="color: red; margin-top: 70px">{{ form.non_field_errors }}</div>
{% endif %}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p class="titleResF1">Название заказа <span data-tooltip data-placement="{% tooltip_placement pk=4 %}" title="{% tooltip pk=4 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ form.name.html_name }}" value="{{ form.name.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p class="titleResF1">Подробно опишите задание <span data-tooltip data-placement="{% tooltip_placement pk=5 %}" title="{% tooltip pk=5 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.text.errors.as_text }}</span></p>
<textarea name="{{ form.text.html_name }}" id="text-new" style="margin-top:0;">{{ form.text.value }}</textarea>
</div>
</div>
<div class="col-lg-3 wrChat1">
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div id="fileUploadContainer" class="col-lg-12 documentsChat">
<div class="upload">
<p id="fileUploadAddBtn" style="margin: 0">+ добавить файл (до 100 файлов)</p>
</div>
<ul class="list-new-new">
<li class="file-upload-widget" style="display: none">
<input type="file" name="new_files" class="file-upload-input" style="position: absolute; top: -1000px; left: -1000px">
<p class="file-upload-label"></p>
<div class="file-upload-remove-btn"></div>
</li>
</ul>
</div>
<p class="type-work">Тип работы <span data-tooltip data-placement="{% tooltip_placement pk=6 %}" title="{% tooltip pk=6 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.work_type.errors.as_text }}</span></p>
<div class="mail-block type-work-inset -project-work-type-radios-container">
{% for id, text in form.work_type.field.choices %}
<div class="inset-mb">
<label>
<input
type="radio"
value="{{ id }}"
{% if form.work_type.value|int == id %}checked{% endif %}
name="{{ form.work_type.html_name }}">
<span></span>
</label>
<p>{{ text }}</p>
</div>
{% endfor %}
</div>
<div class="textAreaBlock2 box-sizing disTab">
<a href="#" onclick="return false" data-toggle="modal" data-target="#projectWorkTypeSuggestionModal" class="new-link new-lw">+ Добавить раздел</a>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенный поиск</p>
<button data-tooltip data-placement="{% tooltip_placement pk=12 %}" title="{% tooltip pk=12 %}"
class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта <span data-tooltip data-placement="{% tooltip_placement pk=13 %}"
title="{% tooltip pk=13 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span
id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.realty.errors.as_text }}</span></div>
<div class="col-lg-3">Наименование <span data-tooltip data-placement="{% tooltip_placement pk=14 %}"
title="{% tooltip pk=14 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span
id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.name.errors.as_text }}</span></div>
<div class="col-lg-3">Классификация здания <span data-tooltip
data-placement="{% tooltip_placement pk=15 %}"
title="{% tooltip pk=15 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span
id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.building_classification.errors.as_text }}</span></div>
<div class="col-lg-3">Вид строительства <span data-tooltip
data-placement="{% tooltip_placement pk=16 %}"
title="{% tooltip pk=16 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span
id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.construction_type.errors.as_text }}</span></div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select
class="selectpicker"
id="realtyId"
name="{{ form.realty.html_name }}">
<option value="" {% if not form.realty.value %}selected="selected"{% endif %}>Создать новый</option>
{% for r in form.realty.field.queryset %}
<option value="{{ r.pk }}"
{% if form.realty.value|int == r.pk %}selected="selected"{% endif %}>{{ r.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-3">
<input
type="text"
id="realtyName"
name="{{ realty_form.name.html_name }}"
class="box-sizing surr surr2"
value="{{ realty_form.name.value }}">
</div>
<div class="col-lg-3">
{{ realty_form.building_classification }}
{# <select#}
{# id="realtyBuildingClassificationId"#}
{# #}
{# name="{{ realty_form.building_classification.html_name }}">#}
{# {% for c in realty_form.building_classification.field.queryset %}#}
{# <option value="{{ c.pk }}" {% if realty_form.building_classification.value|int == c.pk %}selected="selected"{% endif %}>{{ c.name }}</option>#}
{# {% endfor %}#}
{# </select>#}
</div>
<div class="col-lg-3">
<select
id="realtyConstructionTypeId"
class="selectpicker"
name="{{ realty_form.construction_type.html_name }}">
{% for t in realty_form.construction_type.field.queryset %}
<option value="{{ t.pk }}"
{% if realty_form.construction_type.value|int == t.pk %}selected="selected"{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-12 new-filter">
<div class="filter clearfix">
<div class="polsF1 disTab">
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Стадия проекта <span data-tooltip data-placement="{% tooltip_placement pk=7 %}" title="{% tooltip pk=7 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">Стадия проекта</span></div>
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Раздел</div>
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Подраздел</div>
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<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 data-tooltip data-placement="{% tooltip_placement pk=8 %}" title="{% tooltip pk=8 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.budget.errors.as_text }}</span></div>
<!--<div class="col-lg-8"></div>-->
</div>
<div class="searchF1 polsF1 polsFF">
<div class="col-lg-4">
<input type="text" class="box-sizing surr" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
{{ form.currency }}
</div>
<div class="col-lg-3">
{{ form.term_type }}
</div>
<div class="col-lg-5 dog-new">
<label>
<input
type="checkbox"
{% if form.budget_by_agreement.value %}checked{% endif %}
name="{{ form.budget_by_agreement.html_name }}">
<span></span>
</label>
<p>или по договоренности <span data-tooltip data-placement="{% tooltip_placement pk=9 %}" title="{% tooltip pk=9 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span></p>
</div>
</div>
<div class="searchF1 polsF1 polsFF make-new">
<label>
<input
type="checkbox"
{% if form.price_and_term_required.value %}checked{% endif %}
name="{{ form.price_and_term_required.html_name }}">
<span></span>
</label>
<p>Сделать для исполнителей обязательным для заполнения поля цена и срок</p>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Способ оплаты <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.deal_type.errors.as_text }}</span></div>
</div>
<div class="searchF1 polsF1 polsFF radio-afer">
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'secure_deal' %}checked{% endif %}
value="secure_deal">
<span></span>
</label>
<p class="text-afer">
Безопасная сделка (с резервированием бюджета)
</p>
<p class="des-afer">
{% tooltip pk=10 as tooltip10 %}{{ tooltip10|linebreaksbr }}
</p>
</div>
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'direct_payment' %}checked{% endif %}
value="direct_payment">
<span></span>
</label>
<p class="text-afer">
Прямая оплата Исполнителю на его кошелек/счет
</p>
<p class="des-afer">
{% tooltip pk=11 as tooltip11 %}{{ tooltip11|linebreaksbr }}
</p>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенный поиск</p>
<button data-tooltip data-placement="{% tooltip_placement pk=12 %}" title="{% tooltip pk=12 %}" class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта <span data-tooltip data-placement="{% tooltip_placement pk=13 %}" title="{% tooltip pk=13 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.realty.errors.as_text }}</span></div>
<div class="col-lg-3">Наименование <span data-tooltip data-placement="{% tooltip_placement pk=14 %}" title="{% tooltip pk=14 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.name.errors.as_text }}</span></div>
<div class="col-lg-3">Классификация здания <span data-tooltip data-placement="{% tooltip_placement pk=15 %}" title="{% tooltip pk=15 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.building_classification.errors.as_text }}</span></div>
<div class="col-lg-3">Вид строительства <span data-tooltip data-placement="{% tooltip_placement pk=16 %}" title="{% tooltip pk=16 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span><br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.construction_type.errors.as_text }}</span></div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select
class="selectpicker"
id="realtyId"
name="{{ form.realty.html_name }}">
<option value="" {% if not form.realty.value %}selected="selected"{% endif %}>Создать новый</option>
{% for r in form.realty.field.queryset %}
<option value="{{ r.pk }}" {% if form.realty.value|int == r.pk %}selected="selected"{% endif %}>{{ r.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-3">
<input
type="text"
id="realtyName"
name="{{ realty_form.name.html_name }}"
class="box-sizing surr surr2"
value="{{ realty_form.name.value }}">
</div>
<div class="col-lg-3">
{{ realty_form.building_classification }}
{# <select#}
{# id="realtyBuildingClassificationId"#}
{# #}
{# name="{{ realty_form.building_classification.html_name }}">#}
{# {% for c in realty_form.building_classification.field.queryset %}#}
{# <option value="{{ c.pk }}" {% if realty_form.building_classification.value|int == c.pk %}selected="selected"{% endif %}>{{ c.name }}</option>#}
{# {% endfor %}#}
{# </select>#}
</div>
<div class="col-lg-3">
<select
id="realtyConstructionTypeId"
class="selectpicker"
name="{{ realty_form.construction_type.html_name }}">
{% for t in realty_form.construction_type.field.queryset %}
<option value="{{ t.pk }}" {% if realty_form.construction_type.value|int == t.pk %}selected="selected"{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение <span data-tooltip data-placement="{% tooltip_placement pk=18 %}" title="{% tooltip pk=18 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.location.errors.as_text }}</span></div>
</div>
<div class="polsF1 polsF2 disTab">
<div>
<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>
</div>
<input type="hidden" id="chosenLocationId" name="{{ realty_form.location.html_name }}" value="{{ realty_form.location.value }}">
<div class="col-lg-3 make-new">
<label>{{ form.cro }}<span></span></label>
<p>Требуется допуск (СРО) <span data-tooltip data-placement="{% tooltip_placement pk=17 %}" title="{% tooltip pk=17 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span></p>
</div>
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение <span data-tooltip data-placement="{% tooltip_placement pk=18 %}"
title="{% tooltip pk=18 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.location.errors.as_text }}</span></div>
</div>
<div class="polsF1 polsF2 disTab">
<div>
<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>
</div>
<input type="hidden" id="chosenLocationId" name="{{ realty_form.location.html_name }}"
value="{{ realty_form.location.value }}">
<div class="col-lg-3 make-new">
<label>{{ form.cro }}<span></span></label>
<p>Требуется допуск (СРО) <span data-tooltip data-placement="{% tooltip_placement pk=17 %}"
title="{% tooltip pk=17 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span></p>
</div>
</form>
{% include 'partials/footer.html' %}
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>
</div>
</div>
</form>
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}
{% block js_block %}
<script>
// Project work type suggestion modal ---------------------------------
;(function() {
var $modal = $('#projectWorkTypeSuggestionModal')
var $form = $modal.find('.-project-work-type-suggestion-form').first()
var workTypeSuggestionUrl = '/projects/suggest-work-type/'
$modal.find('.-action-button').first().on('click', function($evt) {
$.post(workTypeSuggestionUrl, $form.serialize())
.then(function(res) {
if (res.status === 'success') {
$form.trigger('reset')
$('.-error').text('')
$modal.modal('hide')
$.jGrowl('Предложение успешно отправлено')
} else if (res.status === 'error') {
_.flow(
_.toPairs,
_.each(function(pair) {
var cssSelector = pair[0]
var errors = pair[1]
$(cssSelector).first().text(_.join(' ', errors))
})
)(res.form_errors)
}
<script>
// Project work type suggestion modal ---------------------------------
;
(function () {
var $modal = $('#projectWorkTypeSuggestionModal')
var $form = $modal.find('.-project-work-type-suggestion-form').first()
var workTypeSuggestionUrl = '/projects/suggest-work-type/'
$modal.find('.-action-button').first().on('click', function ($evt) {
$.post(workTypeSuggestionUrl, $form.serialize())
.then(function (res) {
if (res.status === 'success') {
$form.trigger('reset')
$('.-error').text('')
$modal.modal('hide')
$.jGrowl('Предложение успешно отправлено')
} else if (res.status === 'error') {
_.flow(
_.toPairs,
_.each(function (pair) {
var cssSelector = pair[0]
var errors = pair[1]
$(cssSelector).first().text(_.join(' ', errors))
})
)(res.form_errors)
}
})
}())
// Scroll to first form validation error ---------------------------
;(function() {
var hash = $('.-validation-error').filter(function(i, el) {return $(el).text()}).first().attr('id')
if (hash)
window.location.hash = hash
}())
</script>
})
}())
// Scroll to first form validation error ---------------------------
;
(function () {
var hash = $('.-validation-error').filter(function (i, el) {
return $(el).text()
}).first().attr('id')
if (hash)
window.location.hash = hash
}())
</script>
{% endblock %}

@ -1,362 +1,397 @@
{% extends 'partials/base.html' %}
{% load i18n %}
{% block head_css %}
<style>
.-error, .errorlist {color: red}
</style>
<style>
.-error, .errorlist {
color: red
}
</style>
{% endblock %}
{% block content %}
{% include 'partials/modals/project_work_type_suggestion.html' %}
{% include 'partials/header.html' %}
{% include 'partials/modals/project_work_type_suggestion.html' %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">Изменение проекта</p>
<div class="container mainScore">
<div class="row">
<div class="col-lg-12 allProjects">
<p class="titleScore">Изменение проекта</p>
</div>
<form action="{% url 'projects:customer-project-edit' pk=pk %}" method="POST" enctype="multipart/form-data"
novalidate class="-spec-work-type-combo-container">
{% 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">
<p class="new-pp new-pp3">Формирование заказа</p>
{% if form.non_field_errors %}
<div class="new-pp3" style="color: red; margin-top: 70px">{{ form.non_field_errors }}</div>
{% endif %}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название заказа <span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ form.name.html_name }}" value="{{ form.name.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Подробно опишите задание <span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.text.errors.as_text }}</span></p>
<textarea name="{{ form.text.html_name }}" id="text-new">{{ form.text.value }}</textarea>
</div>
</div>
<div class="col-lg-3 wrChat1">
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div id="fileUploadContainer" class="col-lg-12 documentsChat">
<div class="upload">
<p id="fileUploadAddBtn" style="margin: 0">+ добавить файл (до 100 файлов)</p>
</div>
<ul class="list-new-new">
{% for file in form.files.field.queryset.all %}
<li class="existing-file-widget">
<input type="checkbox" name="{{ form.files.html_name }}" value="{{ file.pk }}" checked
style='display: none'>
<p class="file-upload-label">{{ file.file.name|basename }} {{ file.file.size|filesizeformat }}</p>
<div class="existing-file-remove-btn"></div>
</li>
{% endfor %}
<li class="file-upload-widget" style="display: none">
<input type="file" name="new_files" class="file-upload-input"
style="position: absolute; top: -1000px; left: -1000px">
<p class="file-upload-label"></p>
<div class="file-upload-remove-btn"></div>
</li>
</ul>
</div>
<p class="type-work">{% trans 'project_stage0' %} <span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.work_type.errors.as_text }}</span>
</p>
<div class="mail-block type-work-inset -project-work-type-radios-container">
{% for id, text in form.work_type.field.choices %}
<div class="inset-mb">
<label>
<input
type="radio"
value="{{ id }}"
{% if form.work_type.value|int == id %}checked{% endif %}
name="{{ form.work_type.html_name }}">
<span></span>
</label>
<p>{{ text }}</p>
</div>
{% endfor %}
</div>
<div class="textAreaBlock2 box-sizing disTab">
<a href="#" onclick="return false" data-toggle="modal" data-target="#projectWorkTypeSuggestionModal"
class="new-link new-lw">+ Добавить раздел</a>
</div>
</div>
</div>
<div class="col-lg-12 new-filter">
<div class="filter clearfix">
<div class="polsF1 disTab">
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage1' %} <span data-tooltip
data-placement="{% tooltip_placement pk=7 %}"
title="{% tooltip pk=7 %}"
class="-green-glyphicon glyphicon glyphicon-question-sign"></span>
<span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">{% trans 'project_stage2' %}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage3' %}</div>
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage4' %}</div>
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<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 id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.budget.errors.as_text }}</span></div>
<!--<div class="col-lg-8"></div>-->
</div>
<div class="searchF1 polsF1 polsFF">
<div class="col-lg-4">
<input type="text" class="box-sizing surr" name="{{ form.budget.html_name }}"
value="{{ form.budget.value }}">
{{ form.currency }}
</div>
<div class="col-lg-3">
{{ form.term_type }}
</div>
<div class="col-lg-5 dog-new">
<label>
<input
type="checkbox"
{% if form.budget_by_agreement.value %}checked{% endif %}
name="{{ form.budget_by_agreement.html_name }}">
<span></span>
</label>
<p>или по договоренности</p>
</div>
</div>
<div class="searchF1 polsF1 polsFF make-new">
<label>
<input
type="checkbox"
{% if form.price_and_term_required.value %}checked{% endif %}
name="{{ form.price_and_term_required.html_name }}">
<span></span>
</label>
<p>Сделать для исполнителей обязательным для заполнения поля цена и срок</p>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Способ оплаты <span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.deal_type.errors.as_text }}</span>
</div>
</div>
<div class="searchF1 polsF1 polsFF radio-afer">
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'secure_deal' %}checked{% endif %}
value="secure_deal">
<span></span>
</label>
<p class="text-afer">
Безопасная сделка (с резервированием бюджета)
</p>
<p class="des-afer">
{% tooltip pk=10 as tooltip10 %}{{ tooltip10|linebreaksbr }}
</p>
</div>
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'direct_payment' %}checked{% endif %}
value="direct_payment">
<span></span>
</label>
<p class="text-afer">
Прямая оплата Исполнителю на его кошелек/счет
</p>
<p class="des-afer">
{% tooltip pk=11 as tooltip11 %}{{ tooltip11|linebreaksbr }}
</p>
</div>
</div>
<form action="{% url 'projects:customer-project-edit' pk=pk %}" method="POST" enctype="multipart/form-data" novalidate class="-spec-work-type-combo-container">
{% 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">
<p class="new-pp new-pp3">Формирование заказа</p>
{% if form.non_field_errors %}
<div class="new-pp3" style="color: red; margin-top: 70px">{{ form.non_field_errors }}</div>
{% endif %}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название заказа <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ form.name.html_name }}" value="{{ form.name.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Подробно опишите задание <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.text.errors.as_text }}</span></p>
<textarea name="{{ form.text.html_name }}" id="text-new">{{ form.text.value }}</textarea>
</div>
</div>
<div class="col-lg-3 wrChat1">
<div class="messageBlock box-sizing disTab">
<p>Дополнительно</p>
</div>
<div id="fileUploadContainer" class="col-lg-12 documentsChat">
<div class="upload">
<p id="fileUploadAddBtn" style="margin: 0">+ добавить файл (до 100 файлов)</p>
</div>
<ul class="list-new-new">
{% for file in form.files.field.queryset.all %}
<li class="existing-file-widget">
<input type="checkbox" name="{{ form.files.html_name }}" value="{{ file.pk }}" checked style='display: none'>
<p class="file-upload-label">{{ file.file.name|basename }} {{ file.file.size|filesizeformat }}</p>
<div class="existing-file-remove-btn"></div>
</li>
{% endfor %}
<li class="file-upload-widget" style="display: none">
<input type="file" name="new_files" class="file-upload-input" style="position: absolute; top: -1000px; left: -1000px">
<p class="file-upload-label"></p>
<div class="file-upload-remove-btn"></div>
</li>
</ul>
</div>
<p class="type-work">Тип работы <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.work_type.errors.as_text }}</span></p>
<div class="mail-block type-work-inset -project-work-type-radios-container">
{% for id, text in form.work_type.field.choices %}
<div class="inset-mb">
<label>
<input
type="radio"
value="{{ id }}"
{% if form.work_type.value|int == id %}checked{% endif %}
name="{{ form.work_type.html_name }}">
<span></span>
</label>
<p>{{ text }}</p>
</div>
{% endfor %}
</div>
<div class="textAreaBlock2 box-sizing disTab">
<a href="#" onclick="return false" data-toggle="modal" data-target="#projectWorkTypeSuggestionModal" class="new-link new-lw">+ Добавить раздел</a>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенный поиск</p>
<button class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта<br><span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ form.realty.errors.as_text }}</span>
</div>
<div class="col-lg-3">Наименование<br><span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.name.errors.as_text }}</span>
</div>
<div class="col-lg-3">Классификация здания<br><span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.building_classification.errors.as_text }}</span>
</div>
<div class="col-lg-3">Вид строительства<br><span id="{% random_ident %}" class="-validation-error"
style="color: red">{{ realty_form.construction_type.errors.as_text }}</span>
</div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select
class="selectpicker"
id="realtyId"
name="{{ form.realty.html_name }}">
<option value="" {% if not form.realty.value %}selected="selected"{% endif %}>Создать новый</option>
{% for r in form.realty.field.queryset %}
<option value="{{ r.pk }}"
{% if form.realty.value|int == r.pk %}selected="selected"{% endif %}>{{ r.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-12 new-filter">
<div class="filter clearfix">
<div class="polsF1 disTab">
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Стадия проекта <span data-tooltip data-placement="{% tooltip_placement pk=7 %}" title="{% tooltip pk=7 %}" class="-green-glyphicon glyphicon glyphicon-question-sign"></span> <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">Стадия проекта</span></div>
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Раздел</div>
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Подраздел</div>
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<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 id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.budget.errors.as_text }}</span></div>
<!--<div class="col-lg-8"></div>-->
</div>
<div class="searchF1 polsF1 polsFF">
<div class="col-lg-4">
<input type="text" class="box-sizing surr" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
{{ form.currency }}
</div>
<div class="col-lg-3">
{{ form.term_type }}
</div>
<div class="col-lg-5 dog-new">
<label>
<input
type="checkbox"
{% if form.budget_by_agreement.value %}checked{% endif %}
name="{{ form.budget_by_agreement.html_name }}">
<span></span>
</label>
<p>или по договоренности</p>
</div>
</div>
<div class="searchF1 polsF1 polsFF make-new">
<label>
<input
type="checkbox"
{% if form.price_and_term_required.value %}checked{% endif %}
name="{{ form.price_and_term_required.html_name }}">
<span></span>
</label>
<p>Сделать для исполнителей обязательным для заполнения поля цена и срок</p>
</div>
<div class="titleF1 titleF2 disTab">
<div class="col-lg-12">Способ оплаты <span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.deal_type.errors.as_text }}</span></div>
</div>
<div class="searchF1 polsF1 polsFF radio-afer">
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'secure_deal' %}checked{% endif %}
value="secure_deal">
<span></span>
</label>
<p class="text-afer">
Безопасная сделка (с резервированием бюджета)
</p>
<p class="des-afer">
{% tooltip pk=10 as tooltip10 %}{{ tooltip10|linebreaksbr }}
</p>
</div>
<div class="col-lg-6">
<label>
<input
type="radio"
name="{{ form.deal_type.html_name }}"
{% if form.deal_type.value == 'direct_payment' %}checked{% endif %}
value="direct_payment">
<span></span>
</label>
<p class="text-afer">
Прямая оплата Исполнителю на его кошелек/счет
</p>
<p class="des-afer">
{% tooltip pk=11 as tooltip11 %}{{ tooltip11|linebreaksbr }}
</p>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенный поиск</p>
<button class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Выбор объекта<br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ form.realty.errors.as_text }}</span></div>
<div class="col-lg-3">Наименование<br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.name.errors.as_text }}</span></div>
<div class="col-lg-3">Классификация здания<br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.building_classification.errors.as_text }}</span></div>
<div class="col-lg-3">Вид строительства<br><span id="{% random_ident %}" class="-validation-error" style="color: red">{{ realty_form.construction_type.errors.as_text }}</span></div>
</div>
<div class="polsF1 polsF2 disTab">
<div class="col-lg-3">
<select
class="selectpicker"
id="realtyId"
name="{{ form.realty.html_name }}">
<option value="" {% if not form.realty.value %}selected="selected"{% endif %}>Создать новый</option>
{% for r in form.realty.field.queryset %}
<option value="{{ r.pk }}" {% if form.realty.value|int == r.pk %}selected="selected"{% endif %}>{{ r.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-3">
<input
type="text"
id="realtyName"
name="{{ realty_form.name.html_name }}"
class="box-sizing surr surr2"
value="{{ realty_form.name.value }}">
</div>
<div class="col-lg-3">
<select
id="realtyBuildingClassificationId"
class="selectpicker"
name="{{ realty_form.building_classification.html_name }}">
{% for c in realty_form.building_classification.field.queryset %}
<option value="{{ c.pk }}" {% if realty_form.building_classification.value|int == c.pk %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-3">
<select
id="realtyConstructionTypeId"
class="selectpicker"
name="{{ realty_form.construction_type.html_name }}">
{% for t in realty_form.construction_type.field.queryset %}
<option value="{{ t.pk }}" {% if realty_form.construction_type.value|int == t.pk %}selected="selected"{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение <span id="{% random_ident %}" class="-validation-error" 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%">
</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="{{ realty_form.location.html_name }}" value="{{ realty_form.location.value }}">
<div class="col-lg-3 make-new">
<label>
<input
type="checkbox"
{% if form.cro.value %}checked{% endif %}
name="{{ form.cro.html_name }}">
<span></span>
</label>
<p>Требуется допуск СРО</p>
</div>
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Сохранить">
</div>
</div>
<div class="col-lg-3">
<input
type="text"
id="realtyName"
name="{{ realty_form.name.html_name }}"
class="box-sizing surr surr2"
value="{{ realty_form.name.value }}">
</div>
</form>
{% include 'partials/footer.html' %}
<div class="col-lg-3">
<select
id="realtyBuildingClassificationId"
class="selectpicker"
name="{{ realty_form.building_classification.html_name }}">
{% for c in realty_form.building_classification.field.queryset %}
<option value="{{ c.pk }}"
{% if realty_form.building_classification.value|int == c.pk %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
<div class="col-lg-3">
<select
id="realtyConstructionTypeId"
class="selectpicker"
name="{{ realty_form.construction_type.html_name }}">
{% for t in realty_form.construction_type.field.queryset %}
<option value="{{ t.pk }}"
{% if realty_form.construction_type.value|int == t.pk %}selected="selected"{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="titleF1 disTab">
<div class="col-lg-12">Местоположение <span id="{% random_ident %}" class="-validation-error"
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%">
</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="{{ realty_form.location.html_name }}"
value="{{ realty_form.location.value }}">
<div class="col-lg-3 make-new">
<label>
<input
type="checkbox"
{% if form.cro.value %}checked{% endif %}
name="{{ form.cro.html_name }}">
<span></span>
</label>
<p>Требуется допуск СРО</p>
</div>
</div>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Сохранить">
</div>
</div>
</div>
</form>
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}
{% block js_block %}
<script>
// Project work type suggestion modal ---------------------------------
;(function() {
var $modal = $('#projectWorkTypeSuggestionModal')
var $form = $modal.find('.-project-work-type-suggestion-form').first()
var workTypeSuggestionUrl = '/projects/suggest-work-type/'
$modal.find('.-action-button').first().on('click', function($evt) {
$.post(workTypeSuggestionUrl, $form.serialize())
.then(function(res) {
if (res.status === 'success') {
console.log('Success')
$form.trigger('reset')
$('.-error').text('')
$modal.modal('hide')
$.jGrowl('Предложение успешно отправлено')
} else if (res.status === 'error') {
console.log('Error')
_.flow(
_.toPairs,
_.each(function(pair) {
var cssSelector = pair[0]
var errors = pair[1]
$(cssSelector).first().text(_.join(' ', errors))
})
)(res.form_errors)
}
<script>
// Project work type suggestion modal ---------------------------------
;
(function () {
var $modal = $('#projectWorkTypeSuggestionModal')
var $form = $modal.find('.-project-work-type-suggestion-form').first()
var workTypeSuggestionUrl = '/projects/suggest-work-type/'
$modal.find('.-action-button').first().on('click', function ($evt) {
$.post(workTypeSuggestionUrl, $form.serialize())
.then(function (res) {
if (res.status === 'success') {
console.log('Success')
$form.trigger('reset')
$('.-error').text('')
$modal.modal('hide')
$.jGrowl('Предложение успешно отправлено')
} else if (res.status === 'error') {
console.log('Error')
_.flow(
_.toPairs,
_.each(function (pair) {
var cssSelector = pair[0]
var errors = pair[1]
$(cssSelector).first().text(_.join(' ', errors))
})
)(res.form_errors)
}
})
}())
// Scroll to first form validation error ------------------------------
;(function() {
var hash = $('.-validation-error').filter(function(i, el) {return $(el).text()}).first().attr('id')
if (hash)
window.location.hash = hash
}())
</script>
})
}())
// Scroll to first form validation error ------------------------------
;
(function () {
var hash = $('.-validation-error').filter(function (i, el) {
return $(el).text()
}).first().attr('id')
if (hash)
window.location.hash = hash
}())
</script>
{% endblock %}

@ -1,55 +1,56 @@
<div class="modal fade" id="projectWorkTypeSuggestionModal" tabindex="-1">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
<h4 class="modal-title">Предложение нового типа работы</h4>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"><span>&times;</span></button>
<h4 class="modal-title">Предложение нового типа работы</h4>
</div>
<div class="modal-body">
<form action="{% url 'projects:suggest-work-type' %}" method="POST" novalidate
class="-project-work-type-suggestion-form">
{% csrf_token %}
<div>{{ work_type_suggestion_form.non_field_errors }}</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.name.label }}</div>
<div>{{ work_type_suggestion_form.name }}</div>
<div class="-error -error-{{ work_type_suggestion_form.name.html_name }}"></div>
</div>
<div class="modal-body">
<form action="{% url 'projects:suggest-work-type' %}" method="POST" novalidate class="-project-work-type-suggestion-form">
{% csrf_token %}
<div>{{ work_type_suggestion_form.non_field_errors }}</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.name.label }}</div>
<div>{{ work_type_suggestion_form.name }}</div>
<div class="-error -error-{{ work_type_suggestion_form.name.html_name }}"></div>
</div>
</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.commentary.label }}</div>
<div>{{ work_type_suggestion_form.commentary }}</div>
<div class="-error -error-{{ work_type_suggestion_form.commentary.html_name }}"></div>
</div>
</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.email.label }}</div>
<div>{{ work_type_suggestion_form.email }}</div>
<div class="-error -error-{{ work_type_suggestion_form.email.html_name }}"></div>
</div>
</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.username.label }}</div>
<div>{{ work_type_suggestion_form.username }}</div>
<div class="-error -error-{{ work_type_suggestion_form.username.html_name }}"></div>
</div>
</div>
</form>
</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.commentary.label }}</div>
<div>{{ work_type_suggestion_form.commentary }}</div>
<div class="-error -error-{{ work_type_suggestion_form.commentary.html_name }}"></div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-primary -action-button">Предложить</button>
</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.email.label }}</div>
<div>{{ work_type_suggestion_form.email }}</div>
<div class="-error -error-{{ work_type_suggestion_form.email.html_name }}"></div>
</div>
</div>
</div>
<div class="row">
<div class="col-xs-12 projectsBlock">
<div>{{ work_type_suggestion_form.username.label }}</div>
<div>{{ work_type_suggestion_form.username }}</div>
<div class="-error -error-{{ work_type_suggestion_form.username.html_name }}"></div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Отмена</button>
<button type="button" class="btn btn-primary -action-button">Предложить</button>
</div>
</div>
</div>
</div>

@ -1,90 +1,89 @@
{% load common_tags %}
<div class="modal-body">
<form method="post">{% csrf_token %}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название заказа <span style="color: red">{{ worksell_form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ worksell_form.name.html_name }}"
value="{{ worksell_form.name.value }}">
<form method="post">{% csrf_token %}
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Название заказа <span style="color: red">{{ worksell_form.name.errors.as_text }}</span></p>
<input type="text" class="box-sizing" name="{{ worksell_form.name.html_name }}"
value="{{ worksell_form.name.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Подробно опишите задание <span style="color: red">{{ worksell_form.text.errors.as_text }}</span></p>
<textarea name="{{ worksell_form.text.html_name }}" id="text-new">{{ worksell_form.text.value }}</textarea>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<select id="realtyBuildingClassificationId" class="selectpicker"
name="{{ realty_form.building_classification.html_name }}">
{% for c in realty_form.building_classification.field.queryset %}
<option value="{{ c.pk }}"
{% if realty_form.building_classification.value|int == c.pk %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
<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%">
</div>
<div class="col-lg-3">
<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%">
</div>
<input type="hidden" id="chosenSpecId" name="{{ worksell_form.specialization.html_name }}"
value="{{ worksell_form.specialization.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ worksell_form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Подробно опишите задание <span style="color: red">{{ worksell_form.text.errors.as_text }}</span></p>
<textarea name="{{ worksell_form.text.html_name }}" id="text-new">{{ worksell_form.text.value }}</textarea>
<div class="col-lg-4">
{{ worksell_form.budget_type }}
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<select id="realtyBuildingClassificationId" class="selectpicker"
name="{{ realty_form.building_classification.html_name }}">
{% for c in realty_form.building_classification.field.queryset %}
<option value="{{ c.pk }}"
{% if realty_form.building_classification.value|int == c.pk %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Срок выаолнения{{ worksell_form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
<div class="col-lg-4">
{{ worksell_form.term_type }}
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ worksell_form.budget.errors.as_text }}</p>
<select id="realtyConstructionTypeId" class="selectpicker" name="{{ worksell_form.construction_type.html_name }}">
{% for t in realty_form.construction_type.field.queryset %}
<option value="{{ t.pk }}"
{% if realty_form.construction_type.value|int == t.pk %}selected="selected"{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
<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%">
</div>
<div class="col-lg-3">
<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%">
</div>
<input type="hidden" id="chosenSpecId" name="{{ worksell_form.specialization.html_name }}"
value="{{ worksell_form.specialization.value }}">
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ worksell_form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
<div class="col-lg-4">
{{ worksell_form.budget_type }}
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Срок выаолнения{{ worksell_form.budget.errors.as_text }}</p>
<div class="row">
<div class="col-lg-8">
<input type="text" class="box-sizing" name="{{ form.budget.html_name }}" value="{{ form.budget.value }}">
</div>
<div class="col-lg-4">
{{ worksell_form.term_type }}
</div>
</div>
</div>
<div class="textAreaBlock2 text-nn box-sizing disTab">
<p>Бюджет{{ worksell_form.budget.errors.as_text }}</p>
<select id="realtyConstructionTypeId" class="selectpicker" name="{{ worksell_form.construction_type.html_name }}">
{% for t in realty_form.construction_type.field.queryset %}
<option value="{{ t.pk }}"
{% if realty_form.construction_type.value|int == t.pk %}selected="selected"{% endif %}>{{ t.name }}</option>
{% endfor %}
</select>
</div>
<div class="searchF1 polsF1 polsFF links-filter">
<input class="btn-submit-link" type="submit" value="Разместить проект">
</div>
</form>
</form>
</div>
<div class="modal-footer">

File diff suppressed because it is too large Load Diff

@ -1,199 +1,184 @@
{% extends 'partials/base.html' %}
{% load common_tags %}
{% load common_tags i18n %}
{% block content %}
{% include 'partials/header.html' %}
<div class="container mainScore">
<div class="row">
<div class="col-lg-12">
<p class="titleScore">Поиск заказов</p>
{% include 'partials/header.html' %}
<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">
<div class="filter clearfix -spec-work-type-combo-container">
<div class="triangle1"></div>
<div class="titleF1 disTab">
<div class="col-lg-3">{% trans 'project_stage0' %}</div>
</div>
<form action="{% url 'projects:project-filter' %}" method="GET" novalidate>
<div class="col-lg-12">
<div class="filter clearfix -spec-work-type-combo-container">
<div class="triangle1"></div>
<div class="titleF1 disTab">
<div class="col-lg-3">Тип работы</div>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
{{ form.work_type }}
</div>
</div>
<div class="titleF1 disTab">
</div>
<div class="polsF1 disTab">
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Стадия проекта <span style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">Стадия проекта</span></div>
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Раздел</div>
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">Подраздел</div>
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<input type="hidden" name="{{ form.specialization.html_name }}" value="{{ form.specialization.value }}" class="-chosen-spec-id">
</div>
<div class="searchF1 resSearchF1">
<div class="col-lg-6">
<input
type="text"
name="keywords"
onkeydown="event.keyCode === 13 && $(this).closest('form').submit()"
value="{{ form.keywords.value }}"
class="searchInp box-sizing"
placeholder="Ключевые слова">
</div>
<div class="col-lg-3">
<a href="#" class="findReal" onclick="$(this).closest('form').submit(); return false">
найти проект
</a>
</div>
<div class="col-lg-3">
<a href="{% url 'projects:project-filter' %}" class="clearSearch">
Очистить фильтр
</a>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенные поля</p>
<button class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Классификация здания</div>
<div class="col-lg-3">Вид строительства</div>
<div class="col-lg-3"></div>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
{{ realty_form.building_classification }}
</div>
<div class="col-lg-3">
{{ realty_form.construction_type }}
</div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Местоположение</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="{{ realty_form.location.html_name }}" value="{{ realty_form.location.value }}">
</div>
<div class="sro">
<div class="col-lg-12">
<label>{{ form.cro }}<span></span></label>
<p>Требуется допуск (СРО)</p>
</div>
</div>
</div>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
{{ form.work_type }}
</div>
</div>
<div class="titleF1 disTab">
</div>
<div class="polsF1 disTab">
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage1' %} <span
style="color: red">{{ form.specialization.errors.as_text }}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-1" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold"><span class="-dynamic-label">{% trans 'project_stage2' %}</span></div>
<input type="hidden" class="-spec-select -spec-select-level-2" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage3' %}</div>
<input type="hidden" class="-spec-select -spec-select-level-3" style="width: 100%">
</div>
<div class="col-lg-3 -single-spec-select">
<div class="-bold">{% trans 'project_stage4' %}</div>
<input type="hidden" class="-spec-select -spec-select-level-4" style="width: 100%">
</div>
<input type="hidden" name="{{ form.specialization.html_name }}" value="{{ form.specialization.value }}"
class="-chosen-spec-id">
</div>
<div class="searchF1 resSearchF1">
<div class="col-lg-6">
<input
type="text"
name="keywords"
onkeydown="event.keyCode === 13 && $(this).closest('form').submit()"
value="{{ form.keywords.value }}"
class="searchInp box-sizing"
placeholder="Ключевые слова">
</div>
<div class="col-lg-3">
<a href="#" class="findReal" onclick="$(this).closest('form').submit(); return false">
найти проект
</a>
</div>
<div class="col-lg-3">
<a href="{% url 'projects:project-filter' %}" class="clearSearch">
Очистить фильтр
</a>
</div>
</div>
<div class="resSearchF1">
<div class="col-lg-3">
<p class="titleResF1">Расширенные поля</p>
<button class="resButtonF1">
<span class="glyphicon glyphicon-arrow-up" aria-hidden="true"></span>
</button>
</div>
<div class="col-lg-9">
<div class="borderS1"></div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Классификация здания</div>
<div class="col-lg-3">Вид строительства</div>
<div class="col-lg-3"></div>
</div>
<div class="polsF1 disTab">
<div class="col-lg-3">
{{ realty_form.building_classification }}
</div>
<div class="col-lg-3">
{{ realty_form.construction_type }}
</div>
</div>
</div>
<div class="slideRes disTab activeSlide">
<div class="titleF1 disTab">
<div class="col-lg-3">Местоположение</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="{{ realty_form.location.html_name }}"
value="{{ realty_form.location.value }}">
</div>
<div class="sro">
<div class="col-lg-12">
<p class="titleScore">{{ display_msg }}</p>
<div class="linkSort">
<p>Сортировать по:</p>
{% for val, text in form.order_by.field.choices %}
<button type="submit" name="{{ form.order_by.html_name }}" value="{{ val }}">{{ text }}</button>
{% endfor %}
<input type="hidden" name="{{ form.last_order_by.html_name }}" value="{{ last_order_by }}">
<input type="checkbox" name="{{ form.reverse_order.html_name }}" {% if reverse_order %}checked{% endif %} style="display: none">
</div>
<label>{{ form.cro }}<span></span></label>
<p>Требуется допуск (СРО)</p>
</div>
</form>
<div class="projectsBlock disTab">
{% for project in projects %}
<div class="projectPro clearfix">
<div class="col-lg-9 leftPro">
<p class="titlePro">
<a href="{% url 'projects:detail' pk=project.pk %}">{{ project.name }}</a>
</p>
{% if project.realty and project.realty.name %}
<ul class="desPro">
<li>
<a href="?realty={{ project.realty.pk }}">Объект "{{ project.realty.name }}"</a>
</li>
</ul>
{% endif %}
<p class="textPro">{{ project.text|linebreaksbr|truncatechars:300 }}</p>
{% if TEMPLATE_DEBUG %}
<pre><!--
</div>
</div>
</div>
</div>
<div class="col-lg-12">
<p class="titleScore">{{ display_msg }}</p>
<div class="linkSort">
<p>Сортировать по:</p>
{% for val, text in form.order_by.field.choices %}
<button type="submit" name="{{ form.order_by.html_name }}" value="{{ val }}">{{ text }}</button>
{% endfor %}
<input type="hidden" name="{{ form.last_order_by.html_name }}" value="{{ last_order_by }}">
<input type="checkbox" name="{{ form.reverse_order.html_name }}" {% if reverse_order %}checked{% endif %}
style="display: none">
</div>
</div>
</form>
<div class="projectsBlock disTab">
{% for project in projects %}
<div class="projectPro clearfix">
<div class="col-lg-9 leftPro">
<p class="titlePro">
<a href="{% url 'projects:detail' pk=project.pk %}">{{ project.name }}</a>
</p>
{% if project.realty and project.realty.name %}
<ul class="desPro">
<li>
<a href="?realty={{ project.realty.pk }}">Объект "{{ project.realty.name }}"</a>
</li>
</ul>
{% endif %}
<p class="textPro">{{ project.text|linebreaksbr|truncatechars:300 }}</p>
{% if TEMPLATE_DEBUG %}
<pre><!--
--><b>Specialization:</b> {{ project.specialization }}<br><!--
--><br><!--
--><b>Realty location:</b> {{ project.realty.location }}<br><!--
@ -202,44 +187,44 @@
--><br><!--
--><b>Build. classif.:</b> {{ project.realty.building_classification }}<br><!--
--></pre>
{% endif %}
<ul class="listPro">
<li>{{ project.created }}</li>
<li>{{ project.hit_count.hits }}</li>
<li>{{ project.answers.count }}</li>
{% if request.user.is_authenticated %}
<li>{{ project.customer.username }}</li>
{% endif %}
</ul>
</div>
<div class="col-lg-3 rightPro">
<p class="cenaPro">
{{ project.budget }} <i class="{% fa_currency_classes project.currency %}"></i>
</p>
<ul>
{% if project.secure_deal %}
<li>Безопасная сделка</li>
{% endif %}
<li>
{{ project.specialization.name }}
</li>
</ul>
</div>
</div>
{% endfor %}
{% endif %}
<ul class="listPro">
<li>{{ project.created }}</li>
<li>{{ project.hit_count.hits }}</li>
<li>{{ project.answers.count }}</li>
{% if request.user.is_authenticated %}
<li>{{ project.customer.username }}</li>
{% endif %}
</ul>
</div>
<div class="col-lg-12 pagin">
{% include 'partials/pagination.html' %}
<div class="col-lg-3 rightPro">
<p class="cenaPro">
{{ project.budget }} <i class="{% fa_currency_classes project.currency %}"></i>
</p>
<ul>
{% if project.secure_deal %}
<li>Безопасная сделка</li>
{% endif %}
<li>
{{ project.specialization.name }}
</li>
</ul>
</div>
{% include 'partials/footer.html' %}
</div>
</div>
{% endfor %}
</div>
<div class="col-lg-12 pagin">
{% include 'partials/pagination.html' %}
</div>
{% include 'partials/footer.html' %}
</div>
</div>
{% endblock %}

@ -1,36 +1,40 @@
import pydash as _;
from django import template
from pprint import pprint, pformat
import pydash as _; _.map = _.map_; _.filter = _.filter_
from archilance import util
_.map = _.map_;
_.filter = _.filter_
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))
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)
@register.filter
def get_answer(project, contractor):
answer = _.find(project.answers.all(), lambda a: a.author == contractor)
if not answer:
answer = _.find(project.answers.all(), lambda a: a.author == contractor.team)
return answer
return answer
# import code; code.interact(local=dict(globals(), **locals()))

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

@ -1,5 +1,4 @@
from django.conf import urls
from django.views.generic import TemplateView
from .views import (
# ContractorOfferOrder,
@ -32,32 +31,37 @@ app_name = 'projects'
urlpatterns = [
urls.url(r'^$', ProjectFilterView.as_view(), name='project-filter'),
urls.url(r'^create/$', CustomerProjectCreateView.as_view(), name='customer-project-create'),
urls.url(r'^(?P<pk>\d+)/$', ProjectDetailWithAnswerView.as_view(), name='detail'),
urls.url(r'^(?P<pk>\d+)/edit/$', CustomerProjectEditView.as_view(), name='customer-project-edit'),
urls.url(r'^(?P<pk>\d+)/trash/$', CustomerProjectTrashView.as_view(), name='customer-project-trash'),
urls.url(r'^(?P<pk>\d+)/restore/$', CustomerProjectRestoreView.as_view(), name='customer-project-restore'),
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'^create-answer-message/(?P<pk>\d+)/$', ProjectAnswerCreateMessageView.as_view(),
name='create-answer-message'),
urls.url(r'^reject-project-answer/(?P<pk>\d+)/$', RejectProjectAnswerView.as_view(), name='reject-project-answer'),
urls.url(r'^restore-project-answer/(?P<pk>\d+)/$', RestoreProjectAnswerView.as_view(), name='restore-project-answer'),
urls.url(r'^restore-project-answer/(?P<pk>\d+)/$', RestoreProjectAnswerView.as_view(),
name='restore-project-answer'),
urls.url(r'^arbitration/create/$', ArbitrationCreateView.as_view(), name='arbitration-create'),
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+)/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+)/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'^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'),
urls.url(r'^candidate/comparison/(?P<pk>\d+)/$', ProjectComparisonView.as_view(), name='comparison'),
urls.url(r'^customer-offer-order/(?P<answer_id>(\d+))/(?P<project_id>(\d+))/$', CustomerOfferOrderView.as_view(), name='customer-offer-order'),
urls.url(r'^customer-offer-order/(?P<answer_id>(\d+))/(?P<project_id>(\d+))/$', CustomerOfferOrderView.as_view(),
name='customer-offer-order'),
urls.url(r'^suggest-work-type/$', ProjectWorkTypeSuggestionView.as_view(), name='suggest-work-type'),
]

@ -1,26 +1,29 @@
import json
from pprint import pformat
import natsort
import pydash as _;
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied
from django.core.files.base import ContentFile
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from django.core.urlresolvers import reverse, reverse_lazy
from django.db.models import Q, F
from django.db.models import Q
from django.http import HttpResponseForbidden, JsonResponse, HttpResponseRedirect, HttpResponse, Http404
from django.shortcuts import render, get_object_or_404, redirect
from django.views.generic import ListView, DetailView, CreateView, DeleteView, View, UpdateView, TemplateView, FormView
from django.views.generic import DetailView, CreateView, DeleteView, View, UpdateView
from hitcount.models import HitCount
from hitcount.views import HitCountMixin
from pprint import pprint, pformat
import json
import natsort
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
import re
from .mixins import LastAccessMixin
from archilance import util
from archilance.mixins import BaseMixin
from common.mixins import CustomerRequiredMixin, ContractorRequiredMixin, NoCsrfMixin
from common.mixins import NoCsrfMixin
from users.models import User, Team
from work_sell.models import Picture, WorkSell, WorkSellPhoto
from ratings.models import HistoryRating
@ -36,7 +39,6 @@ from .models import (
PortfolioPhoto,
Project,
ProjectFile,
Realty,
TERM_TYPE_MORPHS,
)
@ -63,9 +65,9 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
def get(self, request, *args, **kwargs):
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
context.update({'TERM_TYPE_MORPHS': TERM_TYPE_MORPHS})
project = get_object_or_404(Project, pk=kwargs.get('pk'))
context.update({'project': project})
@ -85,12 +87,12 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
answer = _.first(_.filter(project_answers, lambda a: a.author == team))
context.update({'answer': answer})
# answer.messages.update()
if not answer:
team = util.get_related_or_none(contractor, 'team')
if team:
context.update({'can_answer_as_team': True})
@ -108,22 +110,20 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
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?
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
# Check for duplicate answers:
if answer_as_team and util.has_related(request.user, 'team'):
if project.answers.filter(object_id=request.user.team.pk, content_type__model='team').exists():
raise PermissionDenied('Повторный отклик')
else:
if project.answers.filter(object_id=request.user.pk, content_type__model='user').exists():
raise PermissionDenied('Повторный отклик')
if answer_as_team:
form = self.form_class(request.POST, request=request, answer_as_team=True, project=project)
else:
@ -175,7 +175,7 @@ class ProjectAnswerCreateMessageView(BaseMixin, View):
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request)
answer = get_object_or_404(Answer, pk=kwargs.get('pk'))
if form.is_valid():
@ -206,7 +206,7 @@ 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)
@ -216,28 +216,28 @@ class RejectProjectAnswerView(NoCsrfMixin, LoginRequiredMixin, BaseMixin, View):
if request.user.is_contractor():
answer = None
contractor_answer = util.get_or_none(request.user.contractor_answers, pk=kwargs.get('pk'))
if contractor_answer:
answer = contractor_answer
elif request.user.team:
answer = util.get_or_none(request.user.team.answers, pk=kwargs.get('pk'))
if not answer:
raise Http404
elif request.user.is_customer():
project = get_object_or_404(request.user.customer_projects, answers__pk=kwargs.get('pk'))
answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
answer.rejected = True
answer.save()
candidate = Candidate.objects.filter(answer=answer)
if candidate:
candidate.delete()
messages.info(request, 'Успешный отказ от проекта')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@ -247,23 +247,23 @@ class RestoreProjectAnswerView(NoCsrfMixin, LoginRequiredMixin, BaseMixin, View)
if request.user.is_contractor():
answer = None
contractor_answer = util.get_or_none(request.user.contractor_answers, pk=kwargs.get('pk'))
if contractor_answer:
answer = contractor_answer
elif request.user.team:
answer = util.get_or_none(request.user.team.answers, pk=kwargs.get('pk'))
if not answer:
raise Http404
elif request.user.is_customer():
project = get_object_or_404(request.user.customer_projects, answers__pk=kwargs.get('pk'))
answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
answer.rejected = False
answer.save()
messages.info(request, 'Проект успешно восстановлен')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@ -323,7 +323,7 @@ class ProjectFilterView(BaseMixin, View):
realty__location__lft__gte=location.lft,
realty__location__rght__lte=location.rght,
)
if realty:
projects = projects.filter(realty=realty)
@ -402,11 +402,11 @@ class CustomerProjectCreateView(BaseMixin, View):
def get(self, request, *args, **kwargs):
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
form = self.form_class(request=request)
realty_form = self.realty_form(request=request, prefix='realty_form')
work_type_suggestion_form = self.work_type_suggestion_form(request=request, prefix='work_type_suggestion')
context.update({
'form': form,
'realty_form': realty_form,
@ -416,7 +416,8 @@ class CustomerProjectCreateView(BaseMixin, View):
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 = 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')
@ -433,7 +434,7 @@ class CustomerProjectCreateView(BaseMixin, View):
form.save_m2m()
Order.objects.create(project=project, secure=project.deal_type == 'secure_deal')
for file in request.FILES.getlist('new_files'):
ProjectFile.objects.create(file=file, project=project)
@ -478,16 +479,16 @@ class CustomerProjectEditView(BaseMixin, View):
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
# Prevent editing when project's taken:
project = get_object_or_404(request.user.customer_projects, pk=kwargs.get('pk'))
if project.order.contractor or project.order.team:
raise PermissionDenied('Заказ уже находится в работе')
else:
return super().dispatch(request, *args, **kwargs)
raise PermissionDenied
def get(self, request, *args, **kwargs):
project = get_object_or_404(request.user.customer_projects, pk=kwargs.get('pk'))
form = self.form_class(instance=project, request=request)
@ -501,7 +502,7 @@ class CustomerProjectEditView(BaseMixin, View):
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,
@ -525,7 +526,8 @@ class CustomerProjectEditView(BaseMixin, View):
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.files = form.cleaned_data.get(
'files') # TODO: Should we somehow get rid of this explicit assignment?
project.save()
form.save_m2m()
@ -566,11 +568,10 @@ class CustomerProjectEditView(BaseMixin, View):
class ContractorAnswerArchiveView(View):
def post(self, request, *args, **kwargs):
project_pk = request.POST.get('project_pk')
user_pk = request.POST.get('user_pk')
answer = Answer.objects.filter(project_id=project_pk,object_id=user_pk, content_type__model='user').first()
answer = Answer.objects.filter(project_id=project_pk, object_id=user_pk, content_type__model='user').first()
answer.is_archive = True
answer.save()
redirect_to = request.POST.get('next')
@ -670,28 +671,27 @@ class CustomerProjectDeleteView(View):
class ProjectComparisonView(DetailView):
model = Project
template_name = 'comparison.html'
def get_context_data(self, **kwargs):
c = super().get_context_data(**kwargs)
c['TERM_TYPE_MORPHS'] = TERM_TYPE_MORPHS
return c
def add_candidate(request, answer_id, project_id):
answer = Answer.objects.get(pk=answer_id)
project = Project.objects.get(pk=project_id)
count_answers = Candidate.objects.filter(project=project).count()
count_answers += 1
candidate = Candidate.objects.filter(answer=answer).first()
if not candidate:
Candidate.objects.create(answer=answer, project=project, position=count_answers)
redirect_to = '%s%s' % (reverse('projects:detail', kwargs={'pk': project_id}), '#answers')
return redirect(redirect_to)
@ -811,33 +811,33 @@ class ContractorPortfolioUpdateView(BaseMixin, UpdateView):
def get_success_url(self):
return reverse('projects:contractor-portfolio-detail', kwargs={'pk': self.object.pk})
def form_valid(self, form):
portfolio = form.instance
photos = form.cleaned_data['photos']
# # Doesn't work:
#
# portfolio.photos = photos
# portfolio.save()
PortfolioPhoto.objects.filter(portfolio=portfolio).delete()
for photo in photos:
PortfolioPhoto.objects.create(img=photo.img, portfolio=portfolio)
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
PortfolioPhoto.objects.create(img=new_image, portfolio=portfolio)
live_image.file.delete()
live_image.delete()
return super().form_valid(form)
@ -882,25 +882,24 @@ class ArbitrationCreateView(CreateView):
class ProjectWorkTypeSuggestionView(View):
form_class = ProjectWorkTypeSuggestionForm
template_name = 'customer_project_work_type_suggestion.html'
# def get(self, request, *args, **kwargs):
# form = self.form_class(request=request, prefix='work_type_suggestion')
# context = {'form': form}
# return render(request, self.template_name, context)
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request, prefix='work_type_suggestion')
if form.is_valid():
form.save()
return JsonResponse({'status': 'success'})
else:
form_errors = {'.-error-%s' % bfield.html_name: bfield.errors for bfield in form}
return JsonResponse({
'status': 'error',
'form_errors': form_errors,
})
# import code; code.interact(local=dict(globals(), **locals()))

@ -1,4 +1,5 @@
from django.contrib import admin
from .models import SpecializationRating, HistoryRating
admin.site.register(SpecializationRating)

@ -1,7 +1,8 @@
from django.db import models
from django.utils import timezone
from users.models import User, Team
from specializations.models import Specialization
from users.models import User, Team
class HistoryRating(models.Model):
@ -32,7 +33,8 @@ class HistoryRating(models.Model):
class SpecializationRating(models.Model):
user = models.ForeignKey(User, related_name='specialization_rating', null=True, blank=True)
team = models.ForeignKey(Team, related_name='specialization_rating', null=True, blank=True)
specialization = models.ForeignKey(Specialization, related_name='specialization_rating') # TODO: Pluralize related name
specialization = models.ForeignKey(Specialization,
related_name='specialization_rating') # TODO: Pluralize related name
position = models.PositiveIntegerField(default=0)
def __str__(self):

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

@ -1,3 +1 @@
from django.shortcuts import render
# Create your views here.

@ -1,4 +1,5 @@
from django.contrib import admin
from .models import Review
admin.site.register(Review)

@ -5,4 +5,4 @@ class ReviewConfig(AppConfig):
name = 'reviews'
def ready(self):
import reviews.signals
pass

@ -8,7 +8,7 @@ class ReviewFilterSet(FilterSet):
id = AllLookupsFilter()
text = AllLookupsFilter()
type = AllLookupsFilter()
from_contractor = RelatedFilter('users.filters.UserFilterSet')
from_customer = RelatedFilter('users.filters.UserFilterSet')
target_contractor = RelatedFilter('users.filters.UserFilterSet')

@ -1,9 +1,9 @@
from django import forms
from .models import Review
class ReviewForm(forms.ModelForm):
class Meta:
model = Review
@ -18,4 +18,3 @@ class ReviewForm(forms.ModelForm):
'target_customer',
'from_team',
)

@ -1,5 +1,5 @@
from django.db import models
from django.core.urlresolvers import reverse
from django.db import models
from django.utils import timezone
TYPE_REVIEWS = (
@ -20,7 +20,7 @@ class Review(models.Model):
from_customer = models.ForeignKey('users.User', related_name='customer_reviews', null=True, blank=True)
from_contractor = models.ForeignKey('users.User', related_name='contractor_reviews', null=True, blank=True)
from_team = models.ForeignKey('users.Team', related_name='team_reviews', null=True, blank=True)
def __str__(self):
return str(self.pk)
@ -46,7 +46,7 @@ class Review(models.Model):
class Meta:
verbose_name = 'Отзыв'
verbose_name_plural = 'Отзывы'
unique_together = (
('from_customer', 'target_contractor', 'project'),
('from_customer', 'target_team', 'project'),

@ -1,8 +1,7 @@
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from rest_framework.serializers import ModelSerializer
from .models import Review
from users.serializers import UserSerializer, TeamSerializer
class ReviewSerializer(ModelSerializer):
@ -38,4 +37,3 @@ class ReviewSerializer(ModelSerializer):
return obj.target_contractor.pk
elif obj.target_team:
return obj.target_team.owner.pk

@ -1,7 +1,6 @@
from django.db.models.signals import post_save
from django.dispatch import receiver
from users.models import User, Team
from ratings.models import HistoryRating
from .models import Review
@ -28,4 +27,3 @@ def add_rating_review(sender, instance, created, **kwargs):
order = instance.project.order
order.status = 'completed'
order.save()

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

@ -1,8 +1,8 @@
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import ListView, CreateView, DeleteView
from .models import Review
from .forms import ReviewForm
from .models import Review
class ReviewsView(LoginRequiredMixin, ListView):

@ -6,5 +6,13 @@ from .models import Specialization
class SpecializationAdmin(MPTTModelAdmin):
readonly_fields = ('pk', 'lft', 'rght', 'tree_id', 'level')
list_display = ('pk', 'name', 'order')
def get_queryset(self, request):
qs = super(SpecializationAdmin, self).get_queryset(request)
qs.order_by('order')
return qs
admin.site.register(Specialization, SpecializationAdmin)

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

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-14 20:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('specializations', '0002_auto_20161005_0342'),
]
operations = [
migrations.AddField(
model_name='specialization',
name='order',
field=models.PositiveIntegerField(default=0),
preserve_default=False,
),
]

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-14 20:44
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('specializations', '0003_specialization_order'),
]
operations = [
migrations.AlterModelOptions(
name='specialization',
options={'get_latest_by': 'order', 'ordering': ['order'], 'verbose_name': 'Специализация', 'verbose_name_plural': 'Специализации'},
),
migrations.AddField(
model_name='specialization',
name='selectable',
field=models.BooleanField(default=True),
),
]

@ -1,20 +1,24 @@
from django.db import models
from mptt.models import MPTTModel, TreeForeignKey
from mptt.managers import TreeManager
from mptt.models import MPTTModel, TreeForeignKey
class Specialization(MPTTModel):
name = models.CharField(max_length=500)
parent = TreeForeignKey('self', null=True, blank=True, related_name='children', db_index=True)
order = models.PositiveIntegerField()
selectable = models.BooleanField(default=True)
objects = TreeManager()
def __str__(self):
return self.name
class Meta:
get_latest_by = 'order'
ordering = ['order']
verbose_name = 'Специализация'
verbose_name_plural = 'Специализации'
class MPTTMeta:
order_insertion_by = ['name']
order_insertion_by = ['order', 'name']

@ -6,7 +6,7 @@ from .models import Specialization
class NestedSpecializationSerializer(ModelSerializer):
class Meta:
model = Specialization
fields = (
'children',
'id',
@ -23,15 +23,15 @@ class NestedSpecializationSerializer(ModelSerializer):
class SpecializationSerializer(ModelSerializer):
children = NestedSpecializationSerializer(many=True)
parent = NestedSpecializationSerializer()
class Meta:
model = Specialization
fields = (
'children',
'parent',
'projects',
'id',
'level',
'lft',

@ -1,3 +1 @@
from django.test import TestCase
# Create your tests here.

@ -1,5 +1,4 @@
from django.conf import urls
# from django.contrib.auth.views import login, logout
from .views import SpecListView, SpecChildrenDetailView, test_page, test_spec

@ -1,23 +1,24 @@
from django.shortcuts import render
from django.views.generic import ListView, DetailView
from django.http import HttpResponse
from django.http import JsonResponse
from django.views.generic import ListView, DetailView
from django.views.generic import TemplateView
from django.http import HttpResponse
from .models import Specialization
class SpecListView(ListView):
model = Specialization
template_name = 'specializations/specialization.html'
def get_context_data(self, **kwargs):
context = super(SpecListView, self).get_context_data(**kwargs)
root = Specialization.objects.get(pk=1)
context['root'] = root
context['children'] = root.get_children()
context['roots'] = Specialization.objects.root_nodes()
# context['roots'] = Specialization.objects.filter(name='_root') # Migrate with this enabled
return context
@ -29,14 +30,18 @@ def test_page(request):
children = spec.get_children()
return JsonResponse({"hello": 'yes'})
else:
return JsonResponse({"hello":"python"})
return JsonResponse({"hello": "python"})
import json
def test_spec(request):
spec = Specialization.objects.get(pk=2)
children = spec.get_children()
return HttpResponse(json.dumps(children))
class SpecChildrenDetailView(DetailView):
model = Specialization

@ -11,10 +11,10 @@ from .models import (
class UserAdmin(admin.ModelAdmin):
readonly_fields = ('pk','is_staff')
list_display = ('username', 'email', 'get_groups', 'cro', 'is_active', 'rating','last_time_visit')
readonly_fields = ('pk', 'is_staff')
list_display = ('username', 'email', 'get_groups', 'cro', 'is_active', 'rating', 'last_time_visit')
ordering = ('-rating',)
def get_groups(self, obj):
return ', '.join(g.name for g in obj.groups.all())

@ -5,4 +5,4 @@ class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
pass

@ -1,11 +1,9 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from .models import User
class EmailOrUsernameModelBackend(object):
def authenticate(self, username=None, password=None):
if '@' in username:
kwargs = {'email': username}

@ -1,5 +1,8 @@
import pydash as _;
from rest_framework_filters import FilterSet, RelatedFilter, AllLookupsFilter, MethodFilter
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
from .models import User, Team, ContractorResumeFiles, ContractorResume
@ -14,31 +17,31 @@ class UserFilterSet(FilterSet):
is_customer = MethodFilter()
last_name = AllLookupsFilter()
username = AllLookupsFilter()
projects = RelatedFilter('projects.filters.ProjectFilterSet')
class Meta:
model = User
def filter_is_contractor(self, name, qs, value):
if value == 'true':
# return _.filter(qs, lambda user: user.is_contractor())
return qs.filter(groups__name='Исполнители')
return qs
def filter_is_customer(self, name, qs, value):
if value == 'true':
# return _.filter(qs, lambda user: user.is_customer())
return qs.filter(groups__name='Заказчики')
return qs
class TeamFilterSet(FilterSet):
id = AllLookupsFilter()
name = AllLookupsFilter()
owner = RelatedFilter('users.filters.UserFilterSet')
class Meta:
@ -58,7 +61,7 @@ class ContractorResumeFilesFilterSet(FilterSet):
id = AllLookupsFilter()
img = AllLookupsFilter()
title = AllLookupsFilter()
resume = RelatedFilter('users.filters.ContractorResumeFilterSet')
class Meta:

@ -1,18 +1,22 @@
import itertools
import pydash as _;
from django import forms
from mptt.forms import TreeNodeChoiceField
import itertools
import pydash as _; _.map = _.map_; _.filter = _.filter_
_.map = _.map_;
_.filter = _.filter_
from .models import User, UserFinancialInfo, Team, ContractorResume, ContractorResumeFiles, GENDERS
from common.models import Location, LiveImageUpload
from projects.models import Project, Realty, BuildingClassfication, ConstructionType
from projects.models import Project, BuildingClassfication, ConstructionType
from specializations.models import Specialization
class TeamForm(forms.ModelForm):
class Meta:
model = Team
fields = (
'name',
'owner',
@ -22,7 +26,7 @@ class TeamForm(forms.ModelForm):
class ContractorResumeForm(forms.ModelForm):
class Meta:
model = ContractorResume
fields = (
'text',
)
@ -44,15 +48,15 @@ class UserProfileEditForm(forms.ModelForm):
widget=forms.RadioSelect,
required=False,
)
live_image = forms.ModelChoiceField(
queryset=LiveImageUpload.objects.all(),
required=False,
)
class Meta:
model = User
fields = (
'contractor_specializations',
'contractor_status',
@ -68,16 +72,16 @@ class UserProfileEditForm(forms.ModelForm):
'skype',
'website',
)
widgets = {
# TODO: Use common format with jQueryUI Datepicker:
'date_of_birth': forms.TextInput(attrs={'class': 'datepicker box-sizing surr surr2'}),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
attrs = self.fields['contractor_status'].widget.attrs
attrs['class'] = _.join(_.compact((attrs.get('class'), 'box-sizing surr surr2')), ' ')
@ -87,10 +91,10 @@ class UserProfileBasicInfoEditForm(forms.ModelForm):
queryset=LiveImageUpload.objects.all(),
required=False,
)
class Meta:
model = User
fields = (
'contractor_specializations',
'first_name',
@ -98,7 +102,7 @@ class UserProfileBasicInfoEditForm(forms.ModelForm):
'location',
'patronym',
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
@ -107,7 +111,7 @@ class UserProfileBasicInfoEditForm(forms.ModelForm):
class UserFinancialInfoEditForm(forms.ModelForm):
class Meta:
model = UserFinancialInfo
fields = (
'address',
'credit_card_number',
@ -125,21 +129,21 @@ class UserFinancialInfoEditForm(forms.ModelForm):
'subdivision_code',
'yandex_money',
)
widgets = {
# 'date_of_birth': forms.TextInput(attrs={'class': 'datepicker'}),
# 'passport_issue_date': forms.TextInput(attrs={'class': 'datepicker'}),
'legal_status': forms.RadioSelect(),
'residency': forms.RadioSelect(),
}
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
self.fields['residency'].choices = self.fields['residency'].choices[1:]
self.fields['legal_status'].choices = self.fields['legal_status'].choices[1:]
# self.fields['residency'].empty_label = None
# self.fields['residency'].widget.choices = self.fields['residency'].choices
@ -154,19 +158,18 @@ class ContractorFilterForm(forms.Form):
('rating', 'Рейтингу'),
# ('project_number', 'Кол-ву выполн. проектов'),
)
PARTY_TYPES = (
('all', 'Все'),
('teams', 'Группы'),
('contractors', 'Исполнители'),
)
order_by = forms.ChoiceField(required=False, choices=CONTRACTOR_ORDER_CHOICES)
last_order_by = forms.ChoiceField(required=False, choices=CONTRACTOR_ORDER_CHOICES)
reverse_order = forms.BooleanField(required=False)
try: #TODO: dirty
try: # TODO: dirty
qs = Specialization.objects.root_nodes()[0].get_descendants()
except:
qs = Specialization.objects
@ -185,7 +188,7 @@ class ContractorFilterForm(forms.Form):
queryset=qs,
required=False,
)
# building_classification = forms.ModelChoiceField(
# queryset=BuildingClassfication.objects,
# widget=forms.Select(attrs={'class': 'selectpicker'}),
@ -201,56 +204,54 @@ class ContractorFilterForm(forms.Form):
required=False,
level_indicator='',
)
work_type = forms.ChoiceField(
choices=tuple(itertools.chain((('',''),), Project.WORK_TYPES)),
choices=tuple(itertools.chain((('', ''),), Project.WORK_TYPES)),
widget=forms.Select(attrs={'class': 'selectpicker -project-work-type-select-field'}),
required=False,
)
construction_type = forms.ModelChoiceField(
queryset=ConstructionType.objects,
widget=forms.Select(attrs={'class': 'selectpicker'}),
required=False,
empty_label='',
)
cro = forms.BooleanField(required=False)
party_types = forms.ChoiceField(
choices=PARTY_TYPES,
required=False,
)
last_party_types = forms.ChoiceField(
choices=PARTY_TYPES,
required=False,
)
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
class CustomerProfileProjectRealtyForm(forms.Form):
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request')
self.customer = kwargs.pop('customer')
super().__init__(*args, **kwargs)
realties = _.uniq(tuple(p.realty for p in self.customer.customer_projects.all()))
self.fields['realty'] = forms.ChoiceField(
widget=forms.Select(attrs={
'class': 'selectpicker',
'onchange': "$(this).closest('form').submit()",
}),
choices=(('', 'Все объекты'),) + tuple((r.pk, r.name) for r in realties),
required=False,
)
# import code; code.interact(local=dict(globals(), **locals()))

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save