diff --git a/README.md b/README.md index b3d004a..aade22d 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,11 @@ python manage.py shell_plus --use-pythonrc ``` ---------------------------------------- +Generate data via: + +``` +python manage.py generate_* commands +``` Data generation order: @@ -39,4 +44,20 @@ Data generation order: 11. Portfolios 12. Reviews +or here is right commands in one str: +``` +python manage.py generate_superuser && +python manage.py generate_specializations && +python manage.py generate_locations && +python manage.py generate_perm_groups && +python manage.py generate_build_classifs && +python manage.py generate_constr_types && +python manage.py generate_users && +python manage.py generate_teams && +python manage.py generate_realties && +python manage.py generate_projects && +python manage.py generate_portfolios && +python manage.py generate_reviews +``` + ---------------------------------------- diff --git a/api/views.py b/api/views.py index 69ed315..a2dd477 100755 --- a/api/views.py +++ b/api/views.py @@ -66,11 +66,13 @@ class RealtyViewSet(ModelViewSet): class SpecializationViewSet(ModelViewSet): - queryset = Specialization.objects.root_nodes()[0].get_descendants() - # queryset = Specialization.objects # Migrate with this enabled + try: # TODO: dirty + queryset = Specialization.objects.root_nodes()[0].get_descendants() + except: + queryset = Specialization.objects serializer_class = SpecializationSerializer filter_class = SpecializationFilterSet - + class UserViewSet(ModelViewSet): queryset = User.objects.all() @@ -79,7 +81,9 @@ class UserViewSet(ModelViewSet): class LocationViewSet(ModelViewSet): - queryset = Location.objects.root_nodes()[0].get_descendants() - # queryset = Location.objects # Migrate with this enabled + try: # TODO: dirty + queryset = Location.objects.root_nodes()[0].get_descendants() + except: + queryset = Location.objects serializer_class = LocationSerializer filter_class = LocationFilterSet diff --git a/projects/forms.py b/projects/forms.py index a94c7cd..2e7a293 100644 --- a/projects/forms.py +++ b/projects/forms.py @@ -168,18 +168,40 @@ class ContractorProjectAnswerForm(forms.ModelForm): class Meta: model = Answer + # ('author', 'Relation? True', 'Null? None', 'Blank? None'), + # ('content_type', 'Relation? True', 'Null? False', 'Blank? False'), + # ('portfolios', 'Relation? True', 'Null? False', 'Blank? True'), + # ('project', 'Relation? True', 'Null? False', 'Blank? False'), + # + # ('candidates', 'Relation? True', 'Null? True', 'Blank? None'), + # ('files', 'Relation? True', 'Null? True', 'Blank? None'), + # + # + # ('budget', 'Relation? False', 'Null? False', 'Blank? False'), + # ('created', 'Relation? False', 'Null? False', 'Blank? False'), + # ('currency', 'Relation? False', 'Null? False', 'Blank? False'), + # ('object_id', 'Relation? False', 'Null? False', 'Blank? False'), + # ('term', 'Relation? False', 'Null? False', 'Blank? False'), + # ('term_type', 'Relation? False', 'Null? False', 'Blank? False'), + # ('text', 'Relation? False', 'Null? False', 'Blank? False'), + # ('secure_deal_only', 'Relation? False', 'Null? False', 'Blank? True'), + fields = ( 'budget', 'currency', 'term', 'term_type', 'text', + 'secure_deal_only', + + # 'portfolios', + # 'files', ) - widgets = { - 'currency': forms.Select(attrs={'class': 'selectpicker'}), - 'term_type': forms.Select(attrs={'class': 'selectpicker'}), - } + # widgets = { + # 'currency': forms.Select(attrs={'class': 'selectpicker'}), + # 'term_type': forms.Select(attrs={'class': 'selectpicker'}), + # } class StageForm(forms.ModelForm): diff --git a/projects/templates/project_detail.html b/projects/templates/project_detail.html index ffa3556..f54f489 100644 --- a/projects/templates/project_detail.html +++ b/projects/templates/project_detail.html @@ -144,200 +144,502 @@ {% endif %} + + + + + + + + + + + + + + + + + + + + {% if request.user.is_contractor and True %} +
+ {% csrf_token %} + + +
+
+
+

Стоимость

+ +
+
+ +
+
+ {{ form.currency }} +
+
+ +
+
+

Срок

+ +
+
+ +
+
+ {{ form.term_type }} +
+
+
+ +
+
+
+

Текст

+ +
+
+
+
+ +
+
+

Портфолио

+ +
    + {% for p in request.user.portfolios.all %} +
  • {{ p.name }}
  • + {% endfor %} +
+
+
+ +
+
+ Не более 10 файлов общим объемом до 500 Мб +
+
+ +
+
+ Работаю только по безопасной сделке +
+
+ +
+
+ +
+
+ +
+
+ {% elif request.user.is_contractor and False %} - {% if request.user.is_contractor %} -
- {% csrf_token %} - - -
+ + + + + + +{# {% with answer=tmp_answer %}#} +{#
#} +{#
#} +{#
#} +{# #} +{#
#} +{# execitor-image#} +{#
#} +{#
#} +{#

#} +{# {{ answer.contractor.get_full_name }} []#} +{#

#} +{# #} +{#
Свободен
#} +{#
#} +{#
#} +{# #} +{#
#} +{#
#} +{#

Есть допуск СРО

#} +{#
#} +{#
#} +{#
#} +{#

Цена:#} +{# {{ answer.cost|intcomma }}#} +{# #} +{#

#} +{#

#} +{# Срок: {{ answer.term }} {{ answer.term_type }}#} +{#

#} +{#

Опубликован: {{ answer.created }}

#} +{#
#} +{# #} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{#

#} +{# Иванов Петр Иванович#} +{#

#} +{# #} +{# 13.01.2016 / 21:05#} +{# #} +{#
#} +{# #} +{# #} +{# #} +{# #} +{# #} +{#
#} +{#

Lorem ipsum dolor sit amet

#} +{#
#} +{#
#} +{#
#} +{#
#} +{#
#} +{# {% endwith %}#} + + + + + + + + + +
+
+
+ +
+ execitor-image +
+
+

+ Иванов Петр Иванович [ivanov_petr] +

+ +
Свободен
+
+
+ +
+
+

Есть допуск СРО

+
+
+
+

Цена: + 35 000 + +

+

+ Срок: 3 недели +

+

Опубликован: 22.04.2016

+
+ +
-
-

Стоимость

- +
+
+
+
-
-
-

Бюджет

- {{ form.currency }} +
+
+
+
- -
-
-
-
-

Срок

- - +
+
+
+
-
-
-

Тип срока

- {{ form.term_type }} +
+
+
+
- -
-
-
-
-

Текст

- +
+
+
+

+ Иванов Петр Иванович +

+ + 13.01.2016 / 21:05 + +
+ + + + + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar tempor. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus sapien nunc eget odio. +

-
-
- -
-
-
- {% for p in user.portfolio.all %} -

{{ p }}

- {% endfor %} +
+
+

+ Иванов Петр Иванович +

+ + 13.01.2016 / 21:05 + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar tempor. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus sapien nunc eget odio. +

+ + Ответить +
-
- -
-
+
+
+ + + + + + {% elif request.user.is_customer %} + + + + + +
+
+

Исполнители

+
+
+
+ + +
- - - - - - - {% elif request.user.is_customer %} - - - - - -
-
-

Исполнители

+
+
+ +
+
+

Сравнить кандидатов

+
+
+ +
+

Новые исполнители

+
+ + + + + + + + + + + +
+
+
+ +
+ execitor-image +
+
+

+ Иванов Петр Иванович [ivanov_petr] +

+ +
Свободен
-
-
- - - +
+ +
+
+

Есть допуск СРО

-
-
-
-

Сравнить кандидатов

+
+

Цена: + 35 000 + +

+

+ Срок: 3 недели +

+

Опубликован: 22.04.2016

-
-
-

Новые исполнители

-
-
- {% for answer in project.answers.all %} -
-
-
- execitor-image -
-

- {{ answer.contractor.get_full_name }} [] -

- -
Свободен
+ +
+
+
+
+
+
-
- -
-
-

Есть допуск СРО

+
+
+
+
+
-
-

Цена: - {{ answer.cost|intcomma }} - -

-

- Срок: {{ answer.term }} {{ answer.term_type }} -

-

Опубликован: {{ answer.created }}

+
+
+
+
+
+
- +
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+

+ Иванов Петр Иванович +

+ + 13.01.2016 / 21:05 + +
+ + + + +
+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar tempor. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus sapien nunc eget odio. +

-
-{#
#} -{#
#} -{#

#} -{# Иванов Петр Иванович#} -{#

#} -{# #} -{# 13.0.2016 / 21:05#} -{# #} -{#
#} -{# #} -{# #} -{# #} -{# #} -{# #} -{#
#} -{#

Lorem ipsum dolor sit amet

#} -{#
#} -{#
#} +
+
+
+

+ Иванов Петр Иванович +

+ + 13.01.2016 / 21:05 + +

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod bibendum laoreet. Proin gravida dolor sit amet lacus accumsan et viverra justo commodo. Proin sodales pulvinar tempor. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nam fermentum, nulla luctus pharetra vulputate, felis tellus mollis orci, sed rhoncus sapien nunc eget odio. +

+ + Ответить +
- {% endfor %} +
- {% endif %} -
+
+ {% endif %} diff --git a/projects/urls.py b/projects/urls.py index 5ff98e1..684450e 100644 --- a/projects/urls.py +++ b/projects/urls.py @@ -30,6 +30,7 @@ urlpatterns = [ urls.url(r'^(?P\d+)/delete/$', CustomerProjectDeleteView.as_view(), name='customer-project-delete'), urls.url(r'^(?P\d+)/answer/$', ContractorProjectAnswerView.as_view(), name='contractor-project-answer'), + urls.url(r'^portfolio/create/$', contractor_portfolio_create, name='contractor-portfolio-create'), urls.url(r'^portfolio/(?P\d+)/edit/$', ContractorPortfolioUpdateView.as_view(), name='contractor-portfolio-edit'), diff --git a/projects/views.py b/projects/views.py index 157bd98..f063389 100644 --- a/projects/views.py +++ b/projects/views.py @@ -42,6 +42,8 @@ class ProjectView(BaseMixin, View): project = get_object_or_404(Project, pk=kwargs.get('pk')) context.update({'project': project}) + # context.update({'tmp_answer': Answer.objects.order_by('?').first()}) + # import code; code.interact(local=dict(globals(), **locals())) if request.user.is_authenticated() and request.user.is_contractor(): @@ -323,21 +325,40 @@ class CustomerProjectEditView(BaseMixin, View): class ContractorProjectAnswerView(BaseMixin, View): form_class = ContractorProjectAnswerForm - def post(self, request, *args, **kwargs): - if not request.user.is_authenticated(): - return HttpResponseForbidden() - form = self.form_class(request.POST) - project = get_object_or_404(Project, pk=kwargs.get('pk')) + def dispatch(self, request, *args, **kwargs): + if request.user.is_authenticated() and request.user.is_contractor(): + return super().dispatch(request, *args, **kwargs) + else: + return HttpResponseForbidden('403 Forbidden') + + def post(self, req, *args, **kwargs): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + if form.is_valid(): - return self.form_valid(request, form, project) - return render(request, self.template_name, {'project': project, 'form': self.form_class}) + project = form.cleaned_data.get('pk') + project.state = 'trashed' + project.save() + + messages.info(req, 'OK') + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + + redirect_to = req.POST.get('next') + return redirect(redirect_to) + + - def form_valid(self, request, form, project): - answer = form.save(commit=False) - answer.contractor = request.user - answer.project = project - answer.save() - return HttpResponseRedirect(reverse('projects:detail', kwargs={'pk': project.pk})) + # def post(self, request, *args, **kwargs): + # form = self.form_class(request.POST) + # project = get_object_or_404(Project, pk=kwargs.get('pk')) + # if form.is_valid(): + # answer = form.save(commit=False) + # answer.contractor = request.user + # answer.project = project + # answer.save() + # return HttpResponseRedirect(reverse('projects:detail', kwargs={'pk': project.pk})) + # return render(request, self.template_name, {'project': project, 'form': self.form_class}) + class CustomerProjectTrashView(View): @@ -350,19 +371,16 @@ class CustomerProjectTrashView(View): return HttpResponseForbidden('403 Forbidden') def post(self, req, *args, **kwargs): - if req.user.is_authenticated(): - form = self.form_class(_.merge({}, req.POST, kwargs), req=req) - - if form.is_valid(): - project = form.cleaned_data.get('pk') - project.state = 'trashed' - project.save() - - messages.info(req, 'Проект перемещён в корзину') - else: - messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if form.is_valid(): + project = form.cleaned_data.get('pk') + project.state = 'trashed' + project.save() + + messages.info(req, 'Проект перемещён в корзину') else: - return HttpResponseForbidden('403 Forbidden') + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) redirect_to = req.POST.get('next') return redirect(redirect_to) @@ -371,20 +389,23 @@ class CustomerProjectTrashView(View): class CustomerProjectRestoreView(View): form_class = CustomerProjectRestoreForm - def post(self, req, *args, **kwargs): - if req.user.is_authenticated(): - form = self.form_class(_.merge({}, req.POST, kwargs), req=req) - - if form.is_valid(): - project = form.cleaned_data.get('pk') - project.state = 'active' - project.save() - - messages.info(req, 'Проект восстановлен из корзины') - else: - messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + def dispatch(self, request, *args, **kwargs): + if request.user.is_authenticated() and request.user.is_customer(): + return super().dispatch(request, *args, **kwargs) else: return HttpResponseForbidden('403 Forbidden') + + def post(self, req, *args, **kwargs): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if form.is_valid(): + project = form.cleaned_data.get('pk') + project.state = 'active' + project.save() + + messages.info(req, 'Проект восстановлен из корзины') + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) redirect_to = req.POST.get('next') return redirect(redirect_to) @@ -393,19 +414,22 @@ class CustomerProjectRestoreView(View): class CustomerProjectDeleteView(View): form_class = CustomerProjectDeleteForm - def post(self, req, *args, **kwargs): - if req.user.is_authenticated(): - form = self.form_class(_.merge({}, req.POST, kwargs), req=req) - - if form.is_valid(): - project = form.cleaned_data.get('pk') - project.state = 'deleted' - project.save() - messages.info(req, 'Проект удалён навсегда') - else: - messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) + def dispatch(self, request, *args, **kwargs): + if request.user.is_authenticated() and request.user.is_customer(): + return super().dispatch(request, *args, **kwargs) else: return HttpResponseForbidden('403 Forbidden') + + def post(self, req, *args, **kwargs): + form = self.form_class(_.merge({}, req.POST, kwargs), req=req) + + if form.is_valid(): + project = form.cleaned_data.get('pk') + project.state = 'deleted' + project.save() + messages.info(req, 'Проект удалён навсегда') + else: + messages.info(req, 'Произошла ошибка:
{msg}
'.format(msg=pformat(form.errors))) redirect_to = req.POST.get('next') return redirect(redirect_to) diff --git a/requirements/base.txt b/requirements/base.txt index 60af455..cfc10d3 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -36,6 +36,5 @@ six==1.10.0 sorl-thumbnail==12.3 sqlparse==0.1.19 tornado==4.3 -nats -ort +natsort django-mathfilters diff --git a/users/forms.py b/users/forms.py index c896b34..ffc6acf 100644 --- a/users/forms.py +++ b/users/forms.py @@ -126,16 +126,25 @@ class ContractorFilterForm(forms.Form): 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 + qs = Specialization.objects.root_nodes()[0].get_descendants() + except: + qs = Specialization.objects + specialization = forms.ModelChoiceField( - queryset=Specialization.objects.root_nodes()[0].get_descendants(), - # queryset=Specialization.objects, # Migrate with this enabled + queryset=qs, required=False, ) - + + try: # TODO: dirty + qs = Location.objects.root_nodes()[0].get_descendants() + except: + qs = Location.objects + location = forms.ModelChoiceField( - queryset=Location.objects.root_nodes()[0].get_descendants(), - # queryset=Location.objects, # Migrate with this enabled + queryset=qs, required=False, )