Update all

This commit is contained in:
Krzysztof 2021-02-06 12:31:59 +01:00
parent 7de2d58b80
commit 0251e4deb7
107 changed files with 445421 additions and 337 deletions

View File

@ -10,23 +10,17 @@ class OfferCreateForm(forms.ModelForm):
price = forms.DecimalField(min_value=0.01,max_digits=10, decimal_places=2) price = forms.DecimalField(min_value=0.01,max_digits=10, decimal_places=2)
class Meta: class Meta:
model = Offer model = Offer
fields = ('title','category','price','DMC','Places_to_sleep','cooker','fridge', 'microwave', 'Webasto', 'auto_temp_control', 'water_level_indicator', 'battery_level', 'photovoltaic_panels', 'gearbox', 'tempomat') fields = ('title','category','price','description','DMC','Places_to_sleep','cooker','fridge', 'microwave', 'Webasto', 'auto_temp_control', 'water_level_indicator', 'battery_level', 'photovoltaic_panels', 'gearbox', 'tempomat')
class OfferImageCreateForm(forms.ModelForm): class OfferImageCreateForm(forms.ModelForm):
image = forms.ImageField() image = forms.ImageField(required = True)
main_image = forms.BooleanField(initial=False, required=False)
class Meta: class Meta:
model = Image_offer model = Image_offer
fields = ('image','main_image') fields = ('image','main_image')
class BaseOfferImageFormSet(BaseFormSet): class BaseOfferImageFormSet(BaseFormSet):
def clean(self): pass
if any(self.errors):
return
count = 0
for form in self.forms:
if form.cleaned_data['main_image']:
count += 1
if count > 1:
raise forms.ValidationError(_("Mozna ustawić tylko jedno zdjecie jako miniaturka."))
OfferImageFormSet = formset_factory(OfferImageCreateForm,formset = BaseOfferImageFormSet) OfferImageFormSet = formset_factory(OfferImageCreateForm,formset = BaseOfferImageFormSet)

View File

@ -1,4 +1,4 @@
# Generated by Django 3.1.3 on 2021-01-12 17:44 # Generated by Django 3.1.3 on 2021-02-06 00:07
import camper.models import camper.models
from django.conf import settings from django.conf import settings
@ -15,12 +15,6 @@ class Migration(migrations.Migration):
] ]
operations = [ operations = [
migrations.CreateModel(
name='AlbumImage',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
],
),
migrations.CreateModel( migrations.CreateModel(
name='Offer_Category', name='Offer_Category',
fields=[ fields=[
@ -40,6 +34,7 @@ class Migration(migrations.Migration):
('status', models.CharField(choices=[('Active', 'Active offer'), ('Inactive', 'Inactive offer'), ('Verification', 'Waiting for verication'), ('Blocked', 'Blocked Offer')], default='Inactive', max_length=32)), ('status', models.CharField(choices=[('Active', 'Active offer'), ('Inactive', 'Inactive offer'), ('Verification', 'Waiting for verication'), ('Blocked', 'Blocked Offer')], default='Inactive', max_length=32)),
('title', models.CharField(default='Tytuł oferty', max_length=50, verbose_name='Title')), ('title', models.CharField(default='Tytuł oferty', max_length=50, verbose_name='Title')),
('slug', models.SlugField(default='', editable=False, max_length=70, unique=True)), ('slug', models.SlugField(default='', editable=False, max_length=70, unique=True)),
('description', models.TextField(default='', max_length=1000)),
('price', models.DecimalField(decimal_places=2, max_digits=10)), ('price', models.DecimalField(decimal_places=2, max_digits=10)),
('DMC', models.CharField(choices=[('B', 'Do 3.5 tony.'), ('C', 'Między 3.5 a 7.5 tony'), ('C1', 'Powyzej 7.5 tony')], default='B', max_length=32)), ('DMC', models.CharField(choices=[('B', 'Do 3.5 tony.'), ('C', 'Między 3.5 a 7.5 tony'), ('C1', 'Powyzej 7.5 tony')], default='B', max_length=32)),
('Places_to_sleep', models.IntegerField(default=2)), ('Places_to_sleep', models.IntegerField(default=2)),
@ -53,7 +48,6 @@ class Migration(migrations.Migration):
('photovoltaic_panels', models.BooleanField(default=False)), ('photovoltaic_panels', models.BooleanField(default=False)),
('gearbox', models.CharField(choices=[('Manual', 'Skrzynia manualna'), ('Automat', 'Skrzynia automatyczna')], default='Manual', max_length=32)), ('gearbox', models.CharField(choices=[('Manual', 'Skrzynia manualna'), ('Automat', 'Skrzynia automatyczna')], default='Manual', max_length=32)),
('tempomat', models.BooleanField(default=False)), ('tempomat', models.BooleanField(default=False)),
('album', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='album', to='camper.albumimage', verbose_name='')),
('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='camper.offer_category')), ('category', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='camper.offer_category')),
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
@ -66,7 +60,7 @@ class Migration(migrations.Migration):
name='Image_offer', name='Image_offer',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.ImageField(blank=True, null=True, upload_to=camper.models._upload_path)), ('image', models.ImageField(upload_to=camper.models._upload_path)),
('main_image', models.BooleanField(default=False)), ('main_image', models.BooleanField(default=False)),
('offer', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='camper.offer', verbose_name='')), ('offer', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, related_name='images', to='camper.offer', verbose_name='')),
], ],

View File

@ -1,20 +0,0 @@
# Generated by Django 3.1.3 on 2021-01-12 17:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('camper', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='offer',
name='album',
),
migrations.DeleteModel(
name='AlbumImage',
),
]

View File

@ -1,18 +0,0 @@
# Generated by Django 3.1.3 on 2021-01-24 16:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('camper', '0002_auto_20210112_1748'),
]
operations = [
migrations.AddField(
model_name='offer',
name='description',
field=models.TextField(default='', max_length=1000),
),
]

View File

@ -85,7 +85,7 @@ class Offer(models.Model):
return super(Offer, self).save(*args, **kwargs) return super(Offer, self).save(*args, **kwargs)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('detail_offer_page', kwargs = {'slug': self.slug }) return reverse('offer_detail', kwargs = {'slug': self.slug })
def _upload_path(instance,filename): def _upload_path(instance,filename):

View File

@ -7,8 +7,10 @@ from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
re_path('add-offer/', views.CreateOffer.as_view(), name='create_offer'), re_path('add-offer/', views.CreateOffer.as_view(), name='create_offer'),
#path('offer/<slug:slug>', views.OfferDetailView.as_view(), name = 'offer_detail_view'), path('offer/<slug:slug>', views.OfferDetailView.as_view(), name = 'offer_detail'),
path('update-offer/<slug:slug>',views.EditOffer.as_view(), name = 'update-offer'),
path('offers/', views.OfferListView.as_view(), name = 'list_offers'), path('offers/', views.OfferListView.as_view(), name = 'list_offers'),
#path('offer/<slug:slug>', views.OfferDetailView.as_view(), name = 'detail_offer_page'),
path('', views.Home.as_view(), name='homepage') path('', views.Home.as_view(), name='homepage')

View File

@ -28,21 +28,12 @@ def check_user_is_owner(request):
else: else:
render (request,'upgrade-to-onwer.html') render (request,'upgrade-to-onwer.html')
#def check_offer_belong_to_user(request):
# if request.check_user_is_owner() == True:
# if offer.author == request.users.id:
# return True
# else:
# raise ValueError("To chyba nie twoje :)")
# if check_users_is_owner == True:
# Create your views here.
class CreateOffer(CreateView): class CreateOffer(CreateView):
@method_decorator(login_required) @method_decorator(login_required)
def get(self, request,*args,**kwargs): def get(self, request,*args,**kwargs):
if check_user_is_owner(request) == True or request.user.is_staff == True:
form = OfferCreateForm() form = OfferCreateForm()
formset = OfferImageFormSet() formset = OfferImageFormSet()
context = { context = {
@ -50,8 +41,10 @@ class CreateOffer(CreateView):
'formset': formset, 'formset': formset,
} }
#if check_user_is_owner(request) == True or request.Base_user.is_staff == True: #if check_user_is_owner(request) == True or request.Base_user.is_staff == True:
return render(request,'offer/add-offer.html', context) return render(request,'offer/add-offer.html', context)
else:
messages.error(request, 'Nie masz uprawnień!')
return redirect('/') #Redirect to login / register page for owner
@method_decorator(login_required) @method_decorator(login_required)
def post(self,request,*args,**kwargs): def post(self,request,*args,**kwargs):
@ -64,7 +57,6 @@ class CreateOffer(CreateView):
offer.created_by = request.user offer.created_by = request.user
offer.status = 'Verification' offer.status = 'Verification'
offer.save() offer.save()
for photo in formset: for photo in formset:
try: try:
imageoffer = Image_offer(offer = offer,main_image = photo.cleaned_data['main_image'],image = photo.cleaned_data['image']) imageoffer = Image_offer(offer = offer,main_image = photo.cleaned_data['main_image'],image = photo.cleaned_data['image'])
@ -74,7 +66,6 @@ class CreateOffer(CreateView):
offer.save() offer.save()
messages.error(request,'Dodaj zdjęcia aby przesłać ofertę do weryfikacji') messages.error(request,'Dodaj zdjęcia aby przesłać ofertę do weryfikacji')
return redirect('/') #Redirect to offe-update-page or user's offers list return redirect('/') #Redirect to offe-update-page or user's offers list
messages.success(request,'Oferta utworzona i przesłana do weryfikacji') messages.success(request,'Oferta utworzona i przesłana do weryfikacji')
return redirect('/') #Redirect to offer page or user's offers list return redirect('/') #Redirect to offer page or user's offers list
else: else:
@ -91,8 +82,6 @@ class CreateOffer(CreateView):
class EditOffer(UpdateView): class EditOffer(UpdateView):
model = Offer
def get(self,request,*args, **kwargs): template_name = 'offer/update-offer.html'
pass fields = ('title','category','price','description','DMC','Places_to_sleep','cooker','fridge', 'microwave', 'Webasto', 'auto_temp_control', 'water_level_indicator', 'battery_level', 'photovoltaic_panels', 'gearbox', 'tempomat')
def post(self,request,*args,**kwargs):
pass

View File

@ -2,28 +2,72 @@ from django.shortcuts import render,redirect, get_object_or_404
from django.views.generic import ( from django.views.generic import (
ListView, ListView,
DetailView, DetailView,
View View,
FormView,
CreateView,
) )
from camper.models import Offer, Image_offer from camper.models import Offer, Image_offer,Offer_Category
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from django.core.paginator import Paginator ,EmptyPage, PageNotAnInteger from django.core.paginator import Paginator ,EmptyPage, PageNotAnInteger
from reservation.forms import ReservationForm
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import FormMixin
from reservation.models import Reservation
class Home(View): class Home(View):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
return render(request, 'index.html') context = {}
offers= Offer.objects.filter(status = 'Active').order_by('-date_added')[:2]
context['offers'] = offers
return render(request,'home.html', context=context)
################################################
class OfferDetailView(DetailView): class OfferDetailView(DetailView):
template_name = 'offer/offer-detail.html'
model = Offer model = Offer
context_object_name = 'offer' template_name = 'offer/offer-detail.html'
def get_context_data(self, *args, **kwargs):
context = super(OfferDetailView,self).get_context_data(*args, **kwargs)
context.update({
'form': ReservationForm(),
})
return context
@method_decorator(login_required)
def post(self,request,*args,**kwargs):
self.object = self.get_object(self.get_queryset())
form = ReservationForm(self.request.POST)
if form.is_valid():
reservation = form.save(commit = False)
reservation.user = request.user
reservation.offer = self.object
reservation.status = 'Confirmed'
reservation.save()
messages.success(request,'Twoja rezerwacja została potwierdzona.')
return redirect('/')
else:
context = self.get_context_data(**kwargs)
context.update({'form': ReservationForm()})
messages.error(request, 'Wprowadzone dane są nieprawidłowe.')
return render(request,'offer/offer-detail.html', context)
################################################
class OfferListView(ListView): class OfferListView(ListView):
model = Offer model = Offer
template_name = 'offer/offers.html' template_name = 'offer/offers.html'
context_object_name = 'active_offers_list' context_object_name = 'active_offers_list'
paginate_by = 4 paginate_by = 4
allow_empty_first_page = True
ordering = ['-date_added'] ordering = ['-date_added']
def get_queryset(self): def get_queryset(self):

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
vagus/media/uploads/._default.png Executable file

Binary file not shown.

BIN
vagus/media/uploads/default.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

444938
vagus/mysite.log Executable file

File diff suppressed because it is too large Load Diff

View File

@ -2,10 +2,23 @@ from django.contrib import admin
from .models import Reservation from .models import Reservation
from .forms import ReservationForm from .forms import ReservationForm
class ReservationAdmin(admin.ModelAdmin):
list_display = ('__str__','offer','status')
ordering = ['reservation_start_date']
group_by = ['offer','user']
class CustomReservation(admin.ModelAdmin):
admin.site.register(Reservation, ReservationAdmin) model = Reservation
list_display = ('created_date','user','offer','reservation_start_date','reservation_end_date','reservation_cost')
fieldsets = (
(None, {'fields': ('user','offer','reservation_start_date','reservation_end_date','status','message_to_owner',)}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('user','offer','reservation_start_date','reservation_end_date','status',)}
),
)
ordering = ('offer','user')
search_fields = ('offer','user')
admin.site.register(Reservation, CustomReservation)

View File

@ -3,20 +3,24 @@ from .models import Reservation
from camper.models import Offer from camper.models import Offer
from users.models import Base_User from users.models import Base_User
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from bootstrap_datepicker_plus import DatePickerInput
class ReservationForm(forms.ModelForm): class ReservationForm(forms.ModelForm):
reservation_start_date = forms.DateField(required=True) reservation_start_date = forms.DateField(
reservation_end_date = forms.DateField(required=True,) widget=DatePickerInput(format='%m/%d/%Y')
)
reservation_end_date = forms.DateField(
widget=DatePickerInput(format='%m/%d/%Y')
)
class Meta: class Meta:
model = Reservation model = Reservation
exclude = ('user',) exclude = ('user','offer','status','reservation_cost')
fields = ['reservation_start_date','reservation_end_date','message_to_owner']
widget = {
'reservation_start_date': forms.DateInput(format =['%d/%m/%y']),
'reservation_end_date': forms.DateInput(format =['%d/%m/%y']),
}
class ReservationStatus(forms.ModelForm):
class Meta:
model = Reservation
fields = ['status']

View File

@ -1,4 +1,4 @@
# Generated by Django 3.1.3 on 2021-01-14 00:40 # Generated by Django 3.1.3 on 2021-02-06 09:49
from django.conf import settings from django.conf import settings
from django.db import migrations, models from django.db import migrations, models
@ -11,8 +11,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('camper', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL), migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('camper', '0002_auto_20210112_1748'),
] ]
operations = [ operations = [
@ -20,11 +20,15 @@ class Migration(migrations.Migration):
name='Reservation', name='Reservation',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_date', models.DateTimeField(auto_now_add=True)),
('reservation_start_date', models.DateField(default=django.utils.timezone.now)), ('reservation_start_date', models.DateField(default=django.utils.timezone.now)),
('reservation_end_date', models.DateField()), ('reservation_end_date', models.DateField()),
('reservation_cost', models.DecimalField(decimal_places=2, default=0, max_digits=12)),
('message_to_owner', models.TextField(blank=True, max_length=500, null=True)),
('status', models.CharField(choices=[('Confirmed', 'Rezerwacja potwierdzona'), ('Requsted', 'Oczekuję na potwierdzenie'), ('Cancelled', 'Anulowana'), ('In_progress', 'Trwająca rezerwacja')], max_length=50)), ('status', models.CharField(choices=[('Confirmed', 'Rezerwacja potwierdzona'), ('Requsted', 'Oczekuję na potwierdzenie'), ('Cancelled', 'Anulowana'), ('In_progress', 'Trwająca rezerwacja')], max_length=50)),
('offer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camper.offer', verbose_name='')), ('slug', models.SlugField(default='', editable=False, max_length=255)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='')), ('offer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camper.offer', verbose_name='Offer')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
], ],
), ),
] ]

View File

@ -1,33 +0,0 @@
# Generated by Django 3.1.3 on 2021-01-15 00:37
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('camper', '0002_auto_20210112_1748'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('reservation', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='reservation',
name='offer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='camper.offer', verbose_name='Offer'),
),
migrations.AlterField(
model_name='reservation',
name='reservation_start_date',
field=models.DateTimeField(default=django.utils.timezone.now),
),
migrations.AlterField(
model_name='reservation',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 3.1.3 on 2021-02-06 10:48
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reservation', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='reservation',
name='id',
field=models.AutoField(primary_key=True, serialize=False),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 3.1.3 on 2021-01-24 14:57
from django.db import migrations, models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
('reservation', '0002_auto_20210115_0037'),
]
operations = [
migrations.AlterField(
model_name='reservation',
name='reservation_start_date',
field=models.DateField(default=django.utils.timezone.now),
),
]

View File

@ -6,23 +6,31 @@ from django.core.exceptions import ValidationError
from django.conf import settings from django.conf import settings
from django.core.exceptions import NON_FIELD_ERRORS from django.core.exceptions import NON_FIELD_ERRORS
from django.db.models import Q from django.db.models import Q
from django import forms
from django.contrib import messages
from django.template.defaultfilters import slugify
from django.urls import reverse
STATUS = ( STATUS = (
('Confirmed',_('Rezerwacja potwierdzona')), ('Confirmed',_('Rezerwacja potwierdzona')),
('Requsted',_('Oczekuję na potwierdzenie')), ('Requsted',_('Oczekuję na potwierdzenie')),
('Cancelled',_('Anulowana')), ('Cancelled',_('Anulowana')),
('In_progress',_('Trwająca rezerwacja')),
) )
class Reservation(models.Model): class Reservation(models.Model):
id = models.AutoField(primary_key= True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE) user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name=_("User"), on_delete=models.CASCADE)
offer = models.ForeignKey("camper.Offer", verbose_name=_("Offer"), on_delete=models.CASCADE) offer = models.ForeignKey("camper.Offer", verbose_name=_("Offer"), on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
reservation_start_date = models.DateField(default = timezone.now,) reservation_start_date = models.DateField(default = timezone.now,)
reservation_end_date = models.DateField() reservation_end_date = models.DateField()
reservation_cost = models.DecimalField(max_digits=12,default=0,decimal_places=2)
message_to_owner = models.TextField(max_length=500, blank = True, null = True)
status = models.CharField(choices = STATUS, max_length = 50) status = models.CharField(choices = STATUS, max_length = 50)
slug = models.SlugField(max_length=255,editable = False, default='', unique = False)
def __str__(self): def __str__(self):
return "%s (%s to %s)" % (self.user.email, return "%s (%s to %s)" % (self.user.email,
@ -30,13 +38,38 @@ class Reservation(models.Model):
"%Y/%m/%d"), "%Y/%m/%d"),
self.reservation_end_date.strftime( self.reservation_end_date.strftime(
"%Y/%m/%d"),) "%Y/%m/%d"),)
@property
def get_total_cost(self):
diff_days = self.reservation_end_date - self.reservation_start_date
self.reservation_cost = diff_days.days * self.offer.price
return self.reservation_cost
def clean(self): def clean(self,*args,**kwargs):
errors = {} errors = {}
today_date = timezone.now().date()
if self.created_date != None:
if self.reservation_start_date < today_date:
errors['reservation_start_date'] = "Rezerwacja nie moze rozpoczynać sie w przeszłości"
if self.reservation_end_date < self.reservation_start_date: if self.reservation_end_date < self.reservation_start_date:
errors['reservation_end_date'] = "Data zakończenia wynajmu nie moze byc data wczesniejsza od daty rozpoczecia." errors['reservation_end_date'] = "Data zakończenia wynajmu nie moze byc data wczesniejsza / równa od daty rozpoczecia."
if self.reservation_end_date == self.reservation_start_date:
errors['reservation_end_date'] = "Wynajem nie moze kończyc sie w dniu rozpoczecia wynajmu."
if errors:
raise forms.ValidationError(errors)
def get_absolute_url(self):
return reverse("reservation_detail", kwargs={"slug": self.slug})
def save(self,*args,**kwargs):
errors = {}
filter_params = dict(reservation_end_date__lte=self.reservation_end_date, reservation_start_date__gte=self.reservation_start_date,offer =self.offer) filter_params = dict(reservation_end_date__lte=self.reservation_end_date, reservation_start_date__gte=self.reservation_start_date,offer =self.offer)
if Reservation.objects.filter(**filter_params).exists(): if Reservation.objects.filter(**filter_params):
errors[NON_FIELD_ERRORS] = "Kamper w tym okresie jest juz zarezerwowany." errors[NON_FIELD_ERRORS] = "Kamper w tym okresie jest juz zarezerwowany."
raise ValidationError(errors) if errors:
raise forms.ValidationError(errors)
else:
self.slug = slugify(f'{self.offer.title}-{self.id}-/{self.reservation_start_date}-{self.reservation_end_date}')
return super(Reservation,self).save(*args,**kwargs)

View File

@ -7,28 +7,3 @@ from users.models import Base_User
# Create your tests here. # Create your tests here.
class ReservationCreate(TestCase):
def setUp(self):
testauthor = Base_User.objects.create(email ='test2@vagus.pl', password = '13test45')
testoffer = Offer.objects.create(title = 'tytul',created_by = testauthor,price = 23, category = Offer_Category.objects.get(name = 'CamperVan'))
def test_correct_date(self):
today= datetime.date.today()
ed = datetime.date(year=2021, month=1, day=20)
Reservation.objects.create(
user = Base_User.objects.get(email = 'test2@vagus.pl'),
offer = Offer.objects.get(id = 16),
reservation_start_date = today,
reservation_end_date = ed,
)
def test_incorret_date(self):
today = datetime.date.today()
ed = datetime.date(year = 2020, month=5, day = 15)
Reservation.objects.create(
user = Base_User.objects.get(email = 'test2@vagus.pl'),
offer = Offer.objects.get(id = 16),
reservation_start_date = today,
reservation_end_date = ed,
)

View File

@ -3,9 +3,10 @@ from django.urls import path
from django.conf.urls.static import static from django.conf.urls.static import static
from camper.models import Offer from camper.models import Offer
from . import views from . import views
from django.contrib.auth.decorators import login_required
urlpatterns = [ urlpatterns = [
path('offer/<slug:slug>', views.DetailOfferPage.as_view(), name = 'detail_offer_page'), path('reservation-detail/<slug:slug>', login_required(views.Reservation_Detail.as_view()), name = 'reservation_detail'),
] ]

View File

@ -1,22 +1,12 @@
from django.shortcuts import render,get_object_or_404 from django.shortcuts import render,get_object_or_404
from django.utils.decorators import method_decorator
from django.contrib.auth.decorators import login_required
from .forms import ReservationForm
from camper.models import Offer, Image_offer
from django.views.generic import ( from django.views.generic import (
DetailView DetailView,
UpdateView
) )
from .models import Reservation
from .forms import ReservationStatus
class DetailOfferPage(DetailView): class Reservation_Detail(UpdateView):
model = Offer model = Reservation
template_name = 'offer/offer-detail.html' template_name = 'reservation/reservation-detail.html'
form_class = ReservationStatus
def get_context_data(self, **kwargs):
context = super(DetailOfferPage, self).get_context_data(**kwargs)
context['form'] = ReservationForm
context['Image_offer'] = Image_offer.objects.filter(offer = self.object)
return context
@method_decorator(login_required)
def post(self,request,*args,**kwargs):
pass

View File

@ -20,8 +20,7 @@ body a:hover {
text-decoration: none;} text-decoration: none;}
.container { .container {
width: 1380px; max-width: 1380px;
max-width: 100%;
margin: 0 auto; margin: 0 auto;
} }
@ -118,13 +117,6 @@ body a:hover {
font-weight: 600!important; font-weight: 600!important;
} }
.header-nav-bar-avatar {
min-width: 36px;
max-height: 36px;
padding-left: 10px;
}
.header-nav-bar img { .header-nav-bar img {
width: 36px; width: 36px;
border-radius: 50%; border-radius: 50%;
@ -157,8 +149,8 @@ body a:hover {
.main-content { .main-content {
padding-left: 20px; padding-left: 10px;
padding-right: 20px; padding-right: 10px;
} }
/* OFFERS BOX */ /* OFFERS BOX */
@ -235,7 +227,44 @@ body a:hover {
.offer-detail-row { .offer-detail-row {
display: flex; display: flex;
flex-direction: row;
} }
.offer-detail-content {
min-width: 600px;
}
.offer-detail-status-info {
font-size: 2em;
font-weight: bold;
}
.offer-detail-title-text {
font-size: 1.8em;
}
.offer-detail-description {
margin-top: 25px;
}
.offer-detail-parameters {
margin-top: 25px;
}
.offer-detail-parameters li {
display: inline;
}
.offer-detail-price {
margin:15px;
font-size: 1.5em;
text-align: right;
}
.offer-reservation {
flex:40%;
min-width: 300px;
margin-top: 50px;
}
/* PROFILE */ /* PROFILE */
.profile-wrapper { .profile-wrapper {
@ -250,7 +279,6 @@ body a:hover {
} }
.profile-wrapper-row-information { .profile-wrapper-row-information {
flex:20%; flex:20%;
background-color: crimson;
} }
.profile-wrapper-row-content { .profile-wrapper-row-content {
flex:70%; flex:70%;
@ -295,3 +323,60 @@ body a:hover {
} }
/* HOME PAGE */
.home-header{
background:
linear-gradient(
rgba(0, 0, 0, 0.5),
rgba(0, 0, 0, 0.5)
),
url(/media/ian-usher-Af1OMQpuN14-unsplash.jpg);
background-size: cover;
height: 70vh;
}
.home-header-title {
position: absolute;
font-size: 3em;
top: 45%;
left: 35%;
transform: translate(-50%, -50%);
color: white;
text-align: center;
}
.home-header-button {
border: 1px solid black;
border-radius: 25px;
height: 40px;
width: 300px
}
.home-header-button p {
font-size: 0.5em;
}
.home-row{
display: flex;
justify-content: space-between;
background-color: black;
color: white;
}
.home-grid-offer {
flex:2;
text-align: center;
padding: 50px 25px 50px 25px;
}
.home-grid-offer a, a:hover {
color:white;
}
.home-grid-offer img {
width: 450px;
}
.home-footer-copyright {
width: 100%;
background-color: black;
color: white;
text-align: right;
padding: 50px 10px 1px 10px;
}
.home-footer-copyright p {
font-size: 1em;
}

View File

@ -1,42 +0,0 @@
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<link rel="stylesheet" href = 'style.css'>
<link href = "https://fonts.googleapis.com/css?family=Dosis" rel='stylesheet'>
<link href = "https://fonts.googleapis.com/css?family=Poppins" rel='stylesheet'>
</head>
<body>
<div class='header-smart-baner'> <!-- TOP HEADER SMART BANER-->
<div class='header-smart-baner-text'> Lubisz ciasteczka? Mamy ich naprawdę duzo.</div>
</div> <!-- TOP HEADER SMART BANER END-->
<div class='header'>
<div class='container'>
<div class='header-inner'>
<div class='header-logo'> <a href='#'> vagus </a> </div>
<div class='header-nav'>
<a href='#'>KAMPERY </a>
<a href='#'>O NAS </a>
<a href='#'>FAQ</a>
<a href='#'>KONTAKT </a>
<div class='header-nav-bar'>
<div class='header-nav-bar-container'>
<div class='header-nav-bar-menu-mobile'>
<div class='header-nav-bar-menu-mobile-icon'></div>
<div class='header-nav-bar-menu-mobile-icon'></div>
<div class='header-nav-bar-menu-mobile-icon'></div>
</div>
<div class='header-nav-bar-text'> Witaj, Krzysztof</div>
<div class='header-nav-bar-avatar'> <img src='photo.jpg'> </div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>

33
vagus/template/home.html Executable file
View File

@ -0,0 +1,33 @@
{% extends 'index.html' %}
{% load static %}
{% block content %}
<div class='container'>
<div class='home-wrapper'>
<section class='home-header'>
<div class="home-header-title">
<h1>Stress is caused by</h1>
<p>not camping enough!</p>
<a href = "{{ request.scheme }}://{{ request.META.HTTP_HOST }}/offers"><button class='home-header-button'> <p>Zarezerwuj swój kamper</p> </button> </a>
</div>
</section>
<div class='home-row'>
{% for offer in offers%}
<div class='home-grid-offer'>
<a href ="{{ offer.get_absolute_url}}"> <h4> {{ offer.title}} </h4></a>
{% for photo in offer.images.all %}
{% if photo.main_image == True%}
<img src="{{ photo.image.url }}">
{% endif %}
{% endfor %}
</div>
{% endfor %}
</div>
<div class='home-footer-copyright'>
<p> Copyright by Krzysztof Bonecki 2021 </p>
</div>
</div>
</div>
{% endblock %}

View File

@ -14,7 +14,7 @@
</head> </head>
<body> <body>
<div class='header-smart-baner'> <!-- TOP HEADER SMART BANER--> <div class='header-smart-baner'> <!-- TOP HEADER SMART BANER-->
<div class='header-smart-baner-text'> Lubisz ciasteczka? Mamy ich naprawdę duzo.</div> <div class='header-smart-baner-text'> Lorem Ipsum is simply dummy text of the print...</div>
</div> <!-- TOP HEADER SMART BANER END--> </div> <!-- TOP HEADER SMART BANER END-->
<div class='header'> <div class='header'>
<div class='container'> <div class='container'>
@ -22,12 +22,10 @@
<div class='header-logo'> <a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/'> vagus </a> </div> <div class='header-logo'> <a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/'> vagus </a> </div>
<div class='header-nav'> <div class='header-nav'>
<a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/offers'>KAMPERY </a> <a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/offers'>KAMPERY </a>
<a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/add-offer'>DODAJ OFERTĘ </a> <a href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/add-offer'>DODAJ OFERTĘ </a>
<div class='header-nav-bar'> <div class='header-nav-bar'>
<div class='header-nav-bar-container'> <div class='header-nav-bar-container'>
<div class='header-nav-bar-text'> <div class='header-nav-bar-text'>
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if user.status != 'Moderator' %} {% if user.status != 'Moderator' %}
<a class='link' href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/profile'> Witaj, {{ user.first_name }} </a> <a class='link' href='{{ request.scheme }}://{{ request.META.HTTP_HOST }}/profile'> Witaj, {{ user.first_name }} </a>

View File

@ -38,31 +38,30 @@
</div> </div>
<div class='offer-detail-parameters'> <div class='offer-detail-parameters'>
<ul> <ul class="list-group">
<li> 'DMC' <b> {{offer.DMC}} </b> </li> <li class="list-group-item"> 'DMC' <b> {{offer.DMC}} </b> </li>
<li> 'Places_to_sleep' <b> {{offer.Places_to_sleep}} </b></li> <li class="list-group-item"> 'Places_to_sleep' <b> {{offer.Places_to_sleep}} </b></li>
<li> 'cooker' <b> {{offer.cooker}} </b></li> <li class="list-group-item"> 'cooker' <b> {{offer.cooker}} </b></li>
<li> 'fridge' <b> {{offer.fridge}} </b></li> <li class="list-group-item"> 'fridge' <b> {{offer.fridge}} </b></li>
<li> 'microwave' <b> {{offer.microwave}} </b></li> <li class="list-group-item"> 'microwave' <b> {{offer.microwave}} </b></li>
<li> 'Webasto' <b> {{offer.Webasto}} </b></li> <li class="list-group-item"> 'Webasto' <b> {{offer.Webasto}} </b></li>
<li> 'auto_temp_control' <b> {{offer.auto_temp_control}} </b></li> <li class="list-group-item"> 'auto_temp_control' <b> {{offer.auto_temp_control}} </b></li>
<li> 'water_level_indicator' <b> {{offer.water_level_indicator}} </b> </li> <li class="list-group-item"> 'water_level_indicator' <b> {{offer.water_level_indicator}} </b> </li>
<li> 'battery_level'<b> {{offer.battery_level}} </b> </li> <li class="list-group-item"> 'battery_level'<b> {{offer.battery_level}} </b> </li>
<li> 'photovoltaic_panels' <b> {{offer.photovoltaic_panels}} </b> </li> <li class="list-group-item"> 'photovoltaic_panels' <b> {{offer.photovoltaic_panels}} </b> </li>
<li> 'gearbox <b> {{offer.gearbox}} </b> </li> <li class="list-group-item"> 'gearbox <b> {{offer.gearbox}} </b> </li>
<li> 'tempomat' <b> {{offer.tempomat}} </b> </li> <li class="list-group-item"> 'tempomat' <b> {{offer.tempomat}} </b> </li>
</ul> </ul>
</div> </div>
<div class ='offer-detail-price'> KOSZT: {{offer.price}} PLN / dzień</div>
</div> </div>
</div> </div>
<div class='offer-reservation'> <div class='offer-reservation'>
<form id="form-container" method="POST" class="reservation-form"> <form id="form-container" method="POST" class="reservation-form">
<h3> Zarezerwuj kamper </h3>
{% csrf_token %} {% csrf_token %}
{{ form.media }} {{ form.media }}
{{form.as_p}} {{form.as_p}}
<button style="background-color:black; color:white" class="btn btn-outline-info" type="submit">Wyślij zapytanie</button> <button style="background-color:black; color:white" class="btn btn-outline-info" type="submit">Wyślij zapytanie</button>
</form> </form>
<div> <div>

View File

@ -0,0 +1,11 @@
{% extends 'index.html' %}
{% load static %}
{% block content %}
<form method="post">{% csrf_token %}
{{ form.as_p }}
{{formset.as_p}}
<input type="submit" value="Update">
</form>
{% endblock %}

View File

@ -0,0 +1,30 @@
{% extends 'index.html' %}
{% load static %}
{% block content %}
<table class="table">
<thead>
<tr>
<th scope="col">Tytuł oferty</th>
<th scope="col">Data rezerwacji</th>
<th scope="col">Początek rezerwacji</th>
<th scope="col">Koniec rezerwacji</th>
<th scope ="col"> Koszt </th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<a href="{{ reservation.offer.get_absolute_url }}">{{ reservation.offer.title }}</a>
</td>
<td >{{reservation.user.email}}</td>
<td>{{ reservation.reservation_start_date }}</td>
<td>{{ reservation.reservation_end_date }}</td>
<td> {{ reservation.get_total_cost}} </td>
<td>{{reservation.status}}</td>
</tr>
</tbody>
</table>
{% endblock %}

View File

@ -48,6 +48,37 @@
</tbody> </tbody>
</table> </table>
{% endif%} {% endif%}
{% if reservations %}
<h4> Rezerwacje klientów </h4>
<table class="table ">
<thead>
<tr>
<th scope="col">Wynajęty kamper</th>
<th scope="col">Uzytkownik </th>
<th scope="col">Początek rezerwacji</th>
<th scope="col">Koniec rezerwacji</th>
<th scope="col"> Koszt </th>
<th scope="col">Status</th>
</tr>
</thead>
<tbody>
{% for reservation in reservations %}
<tr>
<td>
<a href="{{ reservation.get_absolute_url }}">{{ reservation.offer.title }}</a>
</td>
<td >{{reservation.user.email}}</td>
<td>{{ reservation.reservation_start_date }}</td>
<td>{{ reservation.reservation_end_date }}</td>
<td> {{ reservation.get_total_cost}} </td>
<td> {{reservation.status}} </td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
{% if user_reservations %} {% if user_reservations %}
<h4> Moje rezerwacje </h4> <h4> Moje rezerwacje </h4>
<table class="table "> <table class="table ">
@ -64,7 +95,7 @@
{% for reservation in user_reservations %} {% for reservation in user_reservations %}
<tr> <tr>
<td> <td>
<a href="{{ reservation.offer.get_absolute_url }}">{{ reservation.offer.title }}</a> <a href="{{ reservation.get_absolute_url }}">{{ reservation.offer.title }}</a>
</td> </td>
<td title="{{ reservation.created_date}}">{{ reservation.created_date }}</td> <td title="{{ reservation.created_date}}">{{ reservation.created_date }}</td>
<td>{{ reservation.reservation_start_date }}</td> <td>{{ reservation.reservation_start_date }}</td>

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