From c96413cae4c5a6a4fd0fd08a6314337677fdebe4 Mon Sep 17 00:00:00 2001 From: PekopT Date: Wed, 3 Aug 2016 17:23:34 +0300 Subject: [PATCH 1/4] #WTF someone has crooked hands --- requirements/base.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 From 136fc3b24f2ca5b1d2ce589336d425e3c1b86a8d Mon Sep 17 00:00:00 2001 From: PekopT Date: Wed, 3 Aug 2016 17:46:42 +0300 Subject: [PATCH 2/4] #WTF migrations fixes --- api/views.py | 14 ++++++++----- .../migrations/0009_auto_20160802_1226.py | 15 ++----------- .../migrations/0009_auto_20160802_1414.py | 1 + .../migrations/0011_auto_20160802_1653.py | 13 +++++++++++- users/forms.py | 21 +++++++++++++------ 5 files changed, 39 insertions(+), 25 deletions(-) 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/migrations/0009_auto_20160802_1226.py b/projects/migrations/0009_auto_20160802_1226.py index a327d92..bc2011b 100644 --- a/projects/migrations/0009_auto_20160802_1226.py +++ b/projects/migrations/0009_auto_20160802_1226.py @@ -2,9 +2,7 @@ # Generated by Django 1.9.7 on 2016-08-02 09:26 from __future__ import unicode_literals -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone +from django.db import migrations class Migration(migrations.Migration): @@ -15,14 +13,5 @@ class Migration(migrations.Migration): ] operations = [ - migrations.AddField( - model_name='order', - name='team', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='team_orders', to='users.Team'), - ), - migrations.AlterField( - model_name='stage', - name='created', - field=models.DateTimeField(default=django.utils.timezone.now), - ), + ] diff --git a/projects/migrations/0009_auto_20160802_1414.py b/projects/migrations/0009_auto_20160802_1414.py index e5c8983..9ba4048 100644 --- a/projects/migrations/0009_auto_20160802_1414.py +++ b/projects/migrations/0009_auto_20160802_1414.py @@ -12,6 +12,7 @@ class Migration(migrations.Migration): dependencies = [ ('contenttypes', '0002_remove_content_type_name'), ('projects', '0008_merge'), + ('projects', '0009_auto_20160802_1226'), ] operations = [ diff --git a/projects/migrations/0011_auto_20160802_1653.py b/projects/migrations/0011_auto_20160802_1653.py index a2ea21e..7384d36 100644 --- a/projects/migrations/0011_auto_20160802_1653.py +++ b/projects/migrations/0011_auto_20160802_1653.py @@ -3,7 +3,8 @@ from __future__ import unicode_literals from django.db import migrations, models - +import django.db.models.deletion +import django.utils.timezone class Migration(migrations.Migration): @@ -22,4 +23,14 @@ class Migration(migrations.Migration): name='secure_deal_only', field=models.BooleanField(default=False), ), + migrations.AddField( + model_name='order', + name='team', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='team_orders', to='users.Team'), + ), + migrations.AlterField( + model_name='stage', + name='created', + field=models.DateTimeField(default=django.utils.timezone.now), + ), ] 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, ) From 7b51de1a09779157242dcde44552ea5e93942d8b Mon Sep 17 00:00:00 2001 From: PekopT Date: Wed, 3 Aug 2016 17:53:58 +0300 Subject: [PATCH 3/4] #WTF fix README --- README.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) 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 +``` + ---------------------------------------- From 7ff07860db7cbf37aa6accea2ada8d90378f8fd6 Mon Sep 17 00:00:00 2001 From: ArturBaybulatov Date: Wed, 3 Aug 2016 19:07:19 +0300 Subject: [PATCH 4/4] #ARC-14 --- projects/forms.py | 30 +- projects/migrations/0012_merge.py | 16 + projects/templates/project_detail.html | 612 ++++++++++++++++++------- projects/urls.py | 1 + projects/views.py | 120 +++-- 5 files changed, 572 insertions(+), 207 deletions(-) create mode 100644 projects/migrations/0012_merge.py 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/migrations/0012_merge.py b/projects/migrations/0012_merge.py new file mode 100644 index 0000000..f684b9f --- /dev/null +++ b/projects/migrations/0012_merge.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-08-02 17:12 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0009_auto_20160802_1226'), + ('projects', '0011_auto_20160802_1653'), + ] + + operations = [ + ] 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)