diff --git a/projects/migrations/0015_auto_20160824_1538.py b/projects/migrations/0015_auto_20160824_1538.py new file mode 100644 index 0000000..f9bd706 --- /dev/null +++ b/projects/migrations/0015_auto_20160824_1538.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-08-24 12:38 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0014_auto_20160823_1912'), + ] + + operations = [ + migrations.AlterModelOptions( + name='portfolio', + options={'ordering': ['-created'], 'verbose_name': 'Портфолио', 'verbose_name_plural': 'Портфолио'}, + ), + migrations.RemoveField( + model_name='project', + name='rejected_answers_count', + ), + migrations.AddField( + model_name='portfolio', + name='created', + field=models.DateTimeField(default=django.utils.timezone.now), + ), + ] diff --git a/projects/models.py b/projects/models.py index 9f4c9b2..bfd5064 100644 --- a/projects/models.py +++ b/projects/models.py @@ -271,11 +271,25 @@ class Portfolio(models.Model): term_type = models.CharField(max_length=20, choices=TERMS, default='hour', null=True, blank=True) user = models.ForeignKey(User, related_name='portfolios', null=True, blank=True) worksell = models.BooleanField(default=False) + created = models.DateTimeField(default=timezone.now) def __str__(self): return self.name + def get_prev(self): + try: + return self.get_previous_by_created() + except self.DoesNotExist: + return None + + def get_next(self): + try: + return self.get_next_by_created() + except self.DoesNotExist: + return None + class Meta: + ordering = ['-created'] verbose_name = 'Портфолио' verbose_name_plural = 'Портфолио' @@ -284,6 +298,7 @@ class Portfolio(models.Model): return photo and photo.img + class PortfolioPhoto(models.Model): img = models.ImageField(upload_to='projects/portfolio') portfolio = models.ForeignKey(Portfolio, related_name='photos') diff --git a/projects/templates/portfolio_detail.html b/projects/templates/portfolio_detail.html new file mode 100644 index 0000000..8d4ae53 --- /dev/null +++ b/projects/templates/portfolio_detail.html @@ -0,0 +1,151 @@ +{% extends 'partials/base.html' %} +{% load staticfiles %} +{% load thumbnail %} +{% block content %} + {% include 'partials/header.html' %} +
{{ object }}
+ + {% if object.get_prev %} +{{ object.budget }}
#} + {#Описание:
++ {{ object.text }} +
+Похожие работы
#} +{#Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
context.update({'form': form})
return render(request, self.template_name, context)
else:
@@ -133,41 +133,41 @@ class ProjectDetailWithAnswerView(BaseMixin, View):
class ProjectAnswerCreateMessageView(BaseMixin, View):
form_class = ProjectAnswerMessageForm
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated():
return super().dispatch(request, *args, **kwargs)
else:
return HttpResponseForbidden('403 Forbidden')
-
+
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST, request=request)
-
+
if form.is_valid():
message = form.save(commit=False)
-
+
if request.user.is_contractor():
answer = get_object_or_404(Answer, pk=kwargs.get('pk'))
-
+
message.answer = answer
message.is_sender_customer = False
-
+
if isinstance(answer.author, User) and answer.author == request.user:
message.contractor_or_team = request.user
elif isinstance(answer.author, Team):
try: team = request.user.team
except Team.DoesNotExist: team = None
-
+
if team and answer.author == team:
message.contractor_or_team = team
elif request.user.is_customer():
answer = get_object_or_404(Answer, pk=kwargs.get('pk')) # TODO: Perform additional checks
message.answer = answer
message.is_sender_customer = True
-
+
message.save()
form.save_m2m()
-
+
messages.info(request, 'Сообщение успешно размещено')
else:
if form.errors:
@@ -175,7 +175,7 @@ class ProjectAnswerCreateMessageView(BaseMixin, View):
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@@ -186,19 +186,19 @@ class RejectProjectAnswerView(BaseMixin, View):
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
-
+
def post(self, request, *args, **kwargs):
if request.user.is_contractor():
answer = get_object_or_404(request.user.contractor_answers, pk=kwargs.get('pk'))
elif request.user.is_customer():
project = get_object_or_404(request.user.projects, answers__pk=kwargs.get('pk'))
answer = get_object_or_404(project.answers, pk=kwargs.get('pk'))
-
+
answer.rejected = True
answer.save()
-
+
messages.info(request, 'Успешный отказ от проекта')
-
+
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@@ -207,107 +207,107 @@ class ProjectFilterView(BaseMixin, View):
template_name = 'project_filter.html'
form_class = ProjectFilterForm
realty_form = ProjectFilterRealtyForm
-
+
def get(self, request, *args, **kwargs):
form = self.form_class(request.GET, request=request)
realty_form = self.realty_form(request.GET, request=request, prefix='realty_form')
context = self.get_context_data(**_.merge({}, request.GET, kwargs))
-
+
projects = Project.objects
-
+
if form.is_valid() and realty_form.is_valid():
ord = None
-
+
keywords = form.cleaned_data.get('keywords')
cro = form.cleaned_data.get('cro')
work_type = form.cleaned_data.get('work_type')
specialization = form.cleaned_data.get('specialization')
-
+
building_classification = realty_form.cleaned_data.get('building_classification')
construction_type = realty_form.cleaned_data.get('construction_type')
location = realty_form.cleaned_data.get('location')
-
+
if keywords:
keywords = tuple(filter(None, re.split(r'\s|,|;', keywords)))
-
+
for k in keywords:
projects = projects.filter(Q(name__icontains=k) | Q(text__icontains=k))
-
+
if cro:
projects = projects.filter(cro=cro)
-
+
if work_type:
projects = projects.filter(work_type=work_type)
-
+
if specialization:
projects = projects.filter(
specialization__lft__gte=specialization.lft,
specialization__rght__lte=specialization.rght,
)
-
+
if building_classification:
projects = projects.filter(realty__building_classification=building_classification)
-
+
if construction_type:
projects = projects.filter(realty__construction_type=construction_type)
-
+
if location:
projects = projects.filter(
realty__location__lft__gte=location.lft,
realty__location__rght__lte=location.rght,
)
-
+
order_by = form.cleaned_data.get('order_by')
last_order_by = form.cleaned_data.get('last_order_by')
reverse_order = form.cleaned_data.get('reverse_order')
-
+
if order_by:
reverse_order = not reverse_order if order_by == last_order_by else False
ord = order_by
last_order_by = ord
elif last_order_by:
ord = last_order_by
-
+
manual_sort = None
-
+
if ord and ord == 'views':
projects = natsort.natsorted(projects.all(), key=lambda p: p.hit_count.hits, reverse=reverse_order)
manual_sort = True
elif ord:
projects = projects.order_by('-%s' % ord if reverse_order else ord)
-
+
context.update({
'last_order_by': last_order_by,
'reverse_order': reverse_order,
})
-
+
project_count = len(projects) if manual_sort else projects.count()
display_msg = 'Найдено %s проектов' % project_count if project_count > 0 else 'Ничего не найдено'
else:
display_msg = 'Пожалуйста, введите корректные данные'
-
+
if form.errors:
messages.info(request, (
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
-
+
paginator = Paginator(projects if manual_sort else projects.all(), settings.PAGE_SIZE)
page = request.GET.get('page')
-
+
try:
projects = paginator.page(page)
except PageNotAnInteger:
projects = paginator.page(1)
except EmptyPage:
projects = paginator.page(paginator.num_pages)
-
+
context.update({
'form': form,
'realty_form': realty_form,
@@ -316,7 +316,7 @@ class ProjectFilterView(BaseMixin, View):
'page_obj': projects,
'display_msg': display_msg,
})
-
+
return render(request, self.template_name, context)
@@ -324,44 +324,44 @@ class CustomerProjectCreateView(BaseMixin, View):
form_class = CustomerProjectEditForm
realty_form = RealtyForm
template_name = 'customer_project_create.html'
-
+
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_customer():
return super().dispatch(request, *args, **kwargs)
else:
raise PermissionDenied
-
+
def get(self, request, *args, **kwargs):
form = self.form_class(request=request)
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})
-
+
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.is_valid()
realty = form.cleaned_data.get('realty')
-
+
if realty:
realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
-
+
if form.is_valid() and realty_form.is_valid():
project = form.save(commit=False)
project.customer = request.user
project.save()
form.save_m2m()
-
+
Order.objects.create(project=project)
-
+
for file in request.FILES.getlist('new_files'):
ProjectFile.objects.create(file=file, project=project)
-
+
if realty:
realty_form.save()
else:
@@ -369,10 +369,10 @@ class CustomerProjectCreateView(BaseMixin, View):
realty.user = request.user
realty.save()
realty_form.save_m2m()
-
+
project.realty = realty # Connect a realty with a project
project.save()
-
+
messages.info(request, 'Проект успешно создан')
redirect_to = reverse('projects:detail', kwargs={'pk': project.pk})
return redirect(redirect_to)
@@ -382,13 +382,13 @@ class CustomerProjectCreateView(BaseMixin, View):
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
-
+
context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
@@ -398,52 +398,52 @@ class CustomerProjectEditView(BaseMixin, View):
form_class = CustomerProjectEditForm
realty_form = RealtyForm
template_name = 'customer_project_edit.html'
-
+
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 get(self, request, *args, **kwargs):
project = get_object_or_404(request.user.projects, pk=kwargs.get('pk'))
form = self.form_class(instance=project, request=request)
-
+
realty = project.realty
-
+
if realty:
realty_form = self.realty_form(instance=project.realty, request=request, prefix='realty_form')
else:
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})
-
+
return render(request, self.template_name, context)
-
+
def post(self, request, *args, **kwargs):
project = get_object_or_404(request.user.projects, pk=kwargs.get('pk'))
form = self.form_class(request.POST, request.FILES, request=request, instance=project)
-
+
form.is_valid()
realty = form.cleaned_data.get('realty')
-
+
if realty:
realty_form = self.realty_form(request.POST, instance=realty, request=request, prefix='realty_form')
else:
realty_form = self.realty_form(request.POST, request=request, prefix='realty_form')
-
+
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.save()
form.save_m2m()
-
+
for file in request.FILES.getlist('new_files'):
proj_file = ProjectFile.objects.create(file=file, project=project)
proj_file.save()
-
+
if realty:
realty_form.save()
else:
@@ -451,10 +451,10 @@ class CustomerProjectEditView(BaseMixin, View):
realty.user = request.user
realty.save()
realty_form.save_m2m()
-
+
project.realty = realty # Connect a realty with a project
project.save()
-
+
messages.info(request, 'Проект успешно отредактирован')
redirect_to = request.POST.get('next')
return redirect(redirect_to)
@@ -464,13 +464,13 @@ class CustomerProjectEditView(BaseMixin, View):
'Произошла ошибка (form)
' '{form}'
).format(form=pformat(form.errors)))
-
+
if realty_form and realty_form.errors:
messages.info(request, (
'Произошла ошибка (realty_form)
' '{realty_form}'
).format(realty_form=pformat(realty_form.errors)))
-
+
context = self.get_context_data(**kwargs)
context.update({'form': form, 'realty_form': realty_form})
return render(request, self.template_name, context)
@@ -506,41 +506,41 @@ class ContractorPortfolioTrashView(View):
class CustomerProjectTrashView(View):
form_class = CustomerProjectTrashForm
-
+
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 = 'trashed'
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)
class CustomerProjectRestoreView(View):
form_class = CustomerProjectRestoreForm
-
+
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'
@@ -549,23 +549,23 @@ class CustomerProjectRestoreView(View):
messages.info(req, 'Проект восстановлен из корзины')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
-
+
redirect_to = req.POST.get('next')
return redirect(redirect_to)
class CustomerProjectDeleteView(View):
form_class = CustomerProjectDeleteForm
-
+
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'
@@ -573,7 +573,7 @@ class CustomerProjectDeleteView(View):
messages.info(req, 'Проект удалён навсегда')
else:
messages.info(req, 'Произошла ошибка: {msg}'.format(msg=pformat(form.errors)))
-
+
redirect_to = req.POST.get('next')
return redirect(redirect_to)
@@ -650,7 +650,7 @@ class OfferOrderView(View):
return HttpResponseRedirect(redirect_url)
-def contractor_portfolio_create(request):
+def contractor_portfolio_create(request): # TODO: pekopt: shit. rewrite using generic
if request.is_ajax():
form = PortfolioForm(data=request.POST)
# import code; code.interact(local=dict(globals(), **locals()))
@@ -707,4 +707,11 @@ class PortfolioDelete(DeleteView):
model = Portfolio
success_url = reverse_lazy('users:contractor-profile')
+
+class PortfolioDetail(DetailView):
+ model = Portfolio
+ template_name = 'portfolio_detail.html'
+
+
+
# import code; code.interact(local=dict(globals(), **locals()))
diff --git a/users/templates/contractor_office.html b/users/templates/contractor_office.html
index 18e87b8..a7e23a1 100644
--- a/users/templates/contractor_office.html
+++ b/users/templates/contractor_office.html
@@ -164,7 +164,7 @@
{% empty %}
diff --git a/users/templates/contractor_profile.html b/users/templates/contractor_profile.html
index 8da283c..89b546f 100644
--- a/users/templates/contractor_profile.html
+++ b/users/templates/contractor_profile.html
@@ -176,8 +176,7 @@
{% endthumbnail %}
{{ p.name }}
- + {{ p }} {% if request.user.pk == contractor.pk %}