From 7d697b3c3a1cd9ba559a395d0bde11d602d49738 Mon Sep 17 00:00:00 2001 From: Levi Date: Tue, 3 May 2016 00:59:40 -0500 Subject: [PATCH 1/5] Added pagination to orders view, Created, Virtual machines booked page ,Changed logged user nabber , Added pagination to virtual machines view , Started Virtual machine detail page --- hosting/managers.py | 8 ++ hosting/migrations/0012_auto_20160501_1850.py | 27 +++++++ hosting/models.py | 21 ++++++ hosting/static/hosting/css/commons.css | 15 ++++ hosting/static/hosting/css/order.css | 17 +++++ hosting/static/hosting/css/orders.css | 5 ++ hosting/templates/hosting/base_short.html | 62 +++++++++++----- .../{invoice.html => order_detail.html} | 48 ++++-------- hosting/templates/hosting/orders.html | 58 +++++++++++++-- .../templates/hosting/virtual_machines.html | 56 ++++++++++++++ hosting/urls.py | 15 ++-- hosting/views.py | 73 ++++++------------- 12 files changed, 289 insertions(+), 116 deletions(-) create mode 100644 hosting/managers.py create mode 100644 hosting/migrations/0012_auto_20160501_1850.py create mode 100644 hosting/static/hosting/css/commons.css create mode 100644 hosting/static/hosting/css/order.css create mode 100644 hosting/static/hosting/css/orders.css rename hosting/templates/hosting/{invoice.html => order_detail.html} (51%) create mode 100644 hosting/templates/hosting/virtual_machines.html diff --git a/hosting/managers.py b/hosting/managers.py new file mode 100644 index 00000000..961b2c6f --- /dev/null +++ b/hosting/managers.py @@ -0,0 +1,8 @@ +from django.db import models + + +class VMPlansManager(models.Manager): + + def active(self, user, **kwargs): + return self.select_related('hostingorder__customer__user').\ + filter(hostingorder__customer__user=user, hostingorder__approved=True, **kwargs) diff --git a/hosting/migrations/0012_auto_20160501_1850.py b/hosting/migrations/0012_auto_20160501_1850.py new file mode 100644 index 00000000..3ef15a46 --- /dev/null +++ b/hosting/migrations/0012_auto_20160501_1850.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-05-01 18:50 +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0011_auto_20160426_0555'), + ] + + operations = [ + migrations.AddField( + model_name='hostingorder', + name='cc_brand', + field=models.CharField(default='Visa', max_length=10), + preserve_default=False, + ), + migrations.AddField( + model_name='hostingorder', + name='last4', + field=models.CharField(default=1111, max_length=4), + preserve_default=False, + ), + ] diff --git a/hosting/models.py b/hosting/models.py index f3e2a178..edee580d 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -2,10 +2,13 @@ import json from django.db import models from django.utils.translation import ugettext_lazy as _ +from django.utils.functional import cached_property from django.core import serializers from membership.models import StripeCustomer from utils.models import BillingAddress +from .managers import VMPlansManager + class RailsBetaUser(models.Model): email = models.EmailField(unique=True) @@ -81,6 +84,12 @@ class VirtualMachinePlan(models.Model): vm_type = models.ForeignKey(VirtualMachineType) price = models.FloatField() + objects = VMPlansManager() + + @cached_property + def hosting_company_name(self): + return self.vm_type.get_hosting_company_display() + @classmethod def create(cls, data, user): instance = cls.objects.create(**data) @@ -88,13 +97,23 @@ class VirtualMachinePlan(models.Model): class HostingOrder(models.Model): + + ORDER_APPROVED_STATUS = 'Approved' + ORDER_DECLINED_STATUS = 'Declined' + VMPlan = models.OneToOneField(VirtualMachinePlan) customer = models.ForeignKey(StripeCustomer) billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) approved = models.BooleanField(default=False) + last4 = models.CharField(max_length=4) + cc_brand = models.CharField(max_length=10) stripe_charge_id = models.CharField(max_length=100, null=True) + @cached_property + def status(self): + return self.ORDER_APPROVED_STATUS if self.approved else self.ORDER_DECLINED_STATUS + @classmethod def create(cls, VMPlan=None, customer=None, billing_address=None): instance = cls.objects.create(VMPlan=VMPlan, customer=customer, @@ -107,6 +126,8 @@ class HostingOrder(models.Model): def set_stripe_charge(self, stripe_charge): self.stripe_charge_id = stripe_charge.id + self.last4 = stripe_charge.source.last4 + self.cc_brand = stripe_charge.source.brand self.save() diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css new file mode 100644 index 00000000..e77ddfa2 --- /dev/null +++ b/hosting/static/hosting/css/commons.css @@ -0,0 +1,15 @@ +.dashboard-container { + padding-top:5%; padding-bottom: 11%; +} + +.borderless td { + border: none !important; +} +.borderless thead { +} + +.borderless tbody:before { + content: "-"; + display: block; + color: transparent; +} diff --git a/hosting/static/hosting/css/order.css b/hosting/static/hosting/css/order.css new file mode 100644 index 00000000..8e0c5919 --- /dev/null +++ b/hosting/static/hosting/css/order.css @@ -0,0 +1,17 @@ +.order-detail-container {padding-top:5%; padding-bottom: 11%;} + +.order-detail-container .invoice-title h2, .invoice-title h3 { + display: inline-block; +} + +.order-detail-container .table > tbody > tr > .no-line { + border-top: none; +} + +.order-detail-container .table > thead > tr > .no-line { + border-bottom: none; +} + +.order-detail-container .table > tbody > tr > .thick-line { + border-top: 2px solid; +} diff --git a/hosting/static/hosting/css/orders.css b/hosting/static/hosting/css/orders.css new file mode 100644 index 00000000..64e95328 --- /dev/null +++ b/hosting/static/hosting/css/orders.css @@ -0,0 +1,5 @@ +.orders-container {padding-top:5%; padding-bottom: 11%;} + +.orders-container .table > tbody > tr > td { + vertical-align: middle; +} \ No newline at end of file diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index fabfa030..266ee484 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -18,6 +18,9 @@ + + + @@ -49,30 +52,51 @@ - + diff --git a/hosting/templates/hosting/invoice.html b/hosting/templates/hosting/order_detail.html similarity index 51% rename from hosting/templates/hosting/invoice.html rename to hosting/templates/hosting/order_detail.html index 302028fe..4d5dcc18 100644 --- a/hosting/templates/hosting/invoice.html +++ b/hosting/templates/hosting/order_detail.html @@ -2,55 +2,37 @@ {% load staticfiles bootstrap3 %} {% block content %} - - -
+
-

Invoice

Order # {{order.id}}

+

Invoice

Order # {{object.id}}


Billed To:

- John Smith
- 1234 Main
- Apt. 4B
- Springfield, ST 54321 + {{user.name}}
+ {{object.billing_address.street_address}},{{order.billing_address.postal_code}}
+ {{object.billing_address.city}}, {{object.billing_address.country}}.
Order Date:
- {{order.created_at}}

+ {{object.created_at}}

+ Status:
+ {{object.status}}

+
Payment Method:
- {{brand}} ending **** {{last4}}
+ {{object.cc_brand}} ending **** {{object.last4}}
{{user.email}}
@@ -63,15 +45,15 @@

Order summary


-

Type {{request.session.vm_specs.hosting_company_name}}

+

Type {{object.VMPlan.hosting_company_name}}


-

Cores {{request.session.vm_specs.cores}}

+

Cores {{object.VMPlan.cores}}


-

Memory {{request.session.vm_specs.memory}} GiB

+

Memory {{object.VMPlan.memory}} GiB


-

Disk space {{request.session.vm_specs.disk_size}} GiB

+

Disk space {{object.VMPlan.disk_size}} GiB


-

Total

{{request.session.vm_specs.final_price}} CHF

+

Total

{{object.VMPlan.price}} CHF

diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index df5e6216..819dec4d 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -1,31 +1,75 @@ {% extends "hosting/base_short.html" %} {% load staticfiles bootstrap3 %} {% block content %} + + +
-
+
- - +
My orders.
+

My Orders

+
+ {% for order in orders %} - + - - - + + + + {% endfor %}
# Date Amount Status
{{order.id}}{{order.id}} {{order.created_at}}{{order.VMPlan.price}}{{order.approved}}
{{order.VMPlan.price}} CHF{% if order.approved %} + Approved + {% else%} + Declined + {% endif%} + + +
+ + {% if is_paginated %} + + {% endif %} +
diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html new file mode 100644 index 00000000..47eb23b1 --- /dev/null +++ b/hosting/templates/hosting/virtual_machines.html @@ -0,0 +1,56 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 %} +{% block content %} +
+
+
+
+ +

Virtual Machines

+
+ + + + + + + + + + {% for vm in vms %} + + + + + + + {% endfor %} + +
IDTypeAmount
{{vm.id}}{{vm.hosting_company_name}}{{vm.price}} CHF + +
+ + {% if is_paginated %} + + {% endif %} + +
+ +
+
+ +
+ +{%endblock%} \ No newline at end of file diff --git a/hosting/urls.py b/hosting/urls.py index 9f18e836..31a3eaea 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -1,19 +1,20 @@ from django.conf.urls import url from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \ - NodeJSHostingView, LoginView, SignupView, IndexView, \ - InvoiceVMView, OrdersHostingView + NodeJSHostingView, LoginView, SignupView, IndexView, \ + OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), url(r'django/?$', DjangoHostingView.as_view(), name='djangohosting'), url(r'nodejs/?$', NodeJSHostingView.as_view(), name='nodejshosting'), - url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'), + url(r'rails/?$', RailsHostingView.as_view(), name='railshosting'), url(r'payment/?$', PaymentVMView.as_view(), name='payment'), - url(r'invoice/?$', InvoiceVMView.as_view(), name='invoice'), - url(r'orders/?$', OrdersHostingView.as_view(), name='orders'), - url(r'login/?$', LoginView.as_view(), name='login'), + url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), + url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), + url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), + url(r'login/?$', LoginView.as_view(), name='login'), url(r'signup/?$', SignupView.as_view(), name='signup'), url(r'^logout/?$', 'django.contrib.auth.views.logout', - {'next_page': '/ungleich_page'}, name='logout') + {'next_page': '/ungleich_page'}, name='logout') ] diff --git a/hosting/views.py b/hosting/views.py index 65bbbcf3..bfdb5d13 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -2,16 +2,12 @@ from django.shortcuts import get_object_or_404, render from django.core.urlresolvers import reverse_lazy, reverse from django.contrib.auth.mixins import LoginRequiredMixin -from django.contrib.auth.decorators import login_required -from django.utils.decorators import method_decorator -from django.views.generic import View, CreateView, FormView -from django.shortcuts import redirect +from django.views.generic import View, CreateView, FormView, ListView, DetailView from django.http import HttpResponseRedirect from django.contrib.auth import authenticate, login from django.conf import settings -from membership.forms import PaymentForm from membership.models import CustomUser, StripeCustomer from utils.stripe_utils import StripeUtils from utils.forms import BillingAddressForm @@ -21,7 +17,6 @@ from .forms import HostingUserSignupForm, HostingUserLoginForm from .mixins import ProcessVMSelectionMixin - class DjangoHostingView(ProcessVMSelectionMixin, View): template_name = "hosting/django.html" @@ -217,60 +212,38 @@ class PaymentVMView(FormView): 'order': order.id, 'billing_address': billing_address.id }) - return HttpResponseRedirect(reverse('hosting:invoice')) + return HttpResponseRedirect(reverse('hosting:orders', kwargs={'pk': order.id})) else: return self.form_invalid(form) -class InvoiceVMView(LoginRequiredMixin, View): - template_name = "hosting/invoice.html" +class OrdersHostingDetailView(LoginRequiredMixin, DetailView): + template_name = "hosting/order_detail.html" login_url = reverse_lazy('hosting:login') - - def get_context_data(self, **kwargs): - charge = self.request.session.get('charge') - order_id = self.request.session.get('order') - billing_address_id = self.request.session.get('billing_address') - last4 = charge.get('source').get('last4') - brand = charge.get('source').get('brand') - - order = get_object_or_404(HostingOrder, pk=order_id) - billing_address = get_object_or_404(BillingAddress, pk=billing_address_id) - - if not charge: - return - - context = { - 'last4': last4, - 'brand': brand, - 'order': order, - 'billing_address': billing_address, - } - return context - - def get(self, request, *args, **kwargs): - - context = self.get_context_data() - - return render(request, self.template_name, context) + model = HostingOrder -class OrdersHostingView(LoginRequiredMixin, View): +class OrdersHostingListView(LoginRequiredMixin, ListView): template_name = "hosting/orders.html" login_url = reverse_lazy('hosting:login') + context_object_name = "orders" + model = HostingOrder + paginate_by = 10 - def get_context_data(self, **kwargs): + def get_queryset(self): user = self.request.user - orders = HostingOrder.objects.filter(customer__user=user) - context = { - 'orders':orders - } - - return context - - def get(self, request, *args, **kwargs): - - context = self.get_context_data() - - return render(request, self.template_name, context) + self.queryset = HostingOrder.objects.filter(customer__user=user) + return super(OrdersHostingListView, self).get_queryset() +class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): + template_name = "hosting/virtual_machines.html" + login_url = reverse_lazy('hosting:login') + context_object_name = "vms" + model = VirtualMachinePlan + paginate_by = 10 + + def get_queryset(self): + user = self.request.user + self.queryset = VirtualMachinePlan.objects.active(user) + return super(VirtualMachinesPlanListView, self).get_queryset() From bbddb48eccbcaa18229c665f8534170e6ad46ef5 Mon Sep 17 00:00:00 2001 From: Levi Date: Tue, 3 May 2016 01:06:43 -0500 Subject: [PATCH 2/5] Changed redirect url after login --- hosting/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hosting/views.py b/hosting/views.py index bfdb5d13..3494c983 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -101,7 +101,7 @@ class IndexView(View): class LoginView(FormView): template_name = 'hosting/login.html' - success_url = reverse_lazy('hosting:login') + success_url = reverse_lazy('hosting:orders') form_class = HostingUserLoginForm moodel = CustomUser From 1486843af010d5a03b0fd6fa048887572b2bbadc Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 4 May 2016 00:16:41 -0500 Subject: [PATCH 3/5] Added VM detail page, Added VM setting page, Added VM pricing page,Added VM orders page, Added VM status page --- hosting/models.py | 9 + hosting/static/hosting/css/commons.css | 5 + .../static/hosting/css/virtual-machine.css | 42 +++++ hosting/templates/hosting/base_short.html | 3 +- hosting/templates/hosting/orders.html | 16 -- .../hosting/virtual_machine_detail.html | 155 ++++++++++++++++++ .../templates/hosting/virtual_machines.html | 4 +- hosting/urls.py | 5 +- hosting/views.py | 7 + 9 files changed, 226 insertions(+), 20 deletions(-) create mode 100644 hosting/static/hosting/css/virtual-machine.css create mode 100644 hosting/templates/hosting/virtual_machine_detail.html diff --git a/hosting/models.py b/hosting/models.py index edee580d..ea650ebb 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -90,6 +90,15 @@ class VirtualMachinePlan(models.Model): def hosting_company_name(self): return self.vm_type.get_hosting_company_display() + @cached_property + def name(self): + name = 'vm-%s' % self.id + return name + + @cached_property + def orders(self): + return [self.hostingorder] + @classmethod def create(cls, data, user): instance = cls.objects.create(**data) diff --git a/hosting/static/hosting/css/commons.css b/hosting/static/hosting/css/commons.css index e77ddfa2..f386cfd6 100644 --- a/hosting/static/hosting/css/commons.css +++ b/hosting/static/hosting/css/commons.css @@ -13,3 +13,8 @@ display: block; color: transparent; } + +.inline-headers h3, .inline-headers h4 { + display: inline-block; + vertical-align: baseline; +} diff --git a/hosting/static/hosting/css/virtual-machine.css b/hosting/static/hosting/css/virtual-machine.css new file mode 100644 index 00000000..570d5118 --- /dev/null +++ b/hosting/static/hosting/css/virtual-machine.css @@ -0,0 +1,42 @@ +.virtual-machine-container .tabs-left, .virtual-machine-container .tabs-right { + border-bottom: none; + padding-top: 2px; +} +.virtual-machine-container .tabs-left { + border-right: 1px solid #ddd; +} +.virtual-machine-container .tabs-right { + border-left: 1px solid #ddd; +} +.virtual-machine-container .tabs-left>li, .virtual-machine-container .tabs-right>li { + float: none; + margin-bottom: 2px; +} +.virtual-machine-container .tabs-left>li { + margin-right: -1px; +} +.virtual-machine-container .tabs-right>li { + margin-left: -1px; +} +.virtual-machine-container .tabs-left>li.active>a, +.virtual-machine-container .tabs-left>li.active>a:hover, +.virtual-machine-container .tabs-left>li.active>a:focus { + border-bottom-color: #ddd; + border-right-color: transparent; +} + +.virtual-machine-container .tabs-right>li.active>a, +.virtual-machine-container .tabs-right>li.active>a:hover, +.virtual-machine-container .tabs-right>li.active>a:focus { + border-bottom: 1px solid #ddd; + border-left-color: transparent; +} +.virtual-machine-container .tabs-left>li>a { + border-radius: 4px 0 0 4px; + margin-right: 0; + display:block; +} +.virtual-machine-container .tabs-right>li>a { + border-radius: 0 4px 4px 0; + margin-right: 0; +} \ No newline at end of file diff --git a/hosting/templates/hosting/base_short.html b/hosting/templates/hosting/base_short.html index 266ee484..339020de 100644 --- a/hosting/templates/hosting/base_short.html +++ b/hosting/templates/hosting/base_short.html @@ -18,9 +18,10 @@ - + + diff --git a/hosting/templates/hosting/orders.html b/hosting/templates/hosting/orders.html index 819dec4d..99c6464f 100644 --- a/hosting/templates/hosting/orders.html +++ b/hosting/templates/hosting/orders.html @@ -2,22 +2,6 @@ {% load staticfiles bootstrap3 %} {% block content %} - -
diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html new file mode 100644 index 00000000..87edd71f --- /dev/null +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -0,0 +1,155 @@ +{% extends "hosting/base_short.html" %} +{% load staticfiles bootstrap3 %} +{% block content %} +
+
+
+
+
+

{{virtual_machine.name}}

+
+ + +
+ +
+
+
+
+

{{virtual_machine.hosting_company_name}}

+
+
+
+
+
+
+
+
+ Cores
+ {{virtual_machine.cores}} +
+
+
+
+ Memory
+ {{virtual_machine.memory}} GiB +
+
+
+
+ Disk
+ {{virtual_machine.disk_size}} GiB +
+
+
+
+
+ + +
+
+ +
+
+

Current pricing

+ {{virtual_machine.price|floatformat}} CHF/mo +
+
+
+
+
+
+
+ +

Orders

+
+ + + + + + + + + + + {% for order in virtual_machine.orders %} + + + + + + + + {% endfor %} + +
#DateAmountStatus
{{order.id}}{{order.created_at}}{{order.VMPlan.price}} CHF{% if order.approved %} + Approved + {% else%} + Declined + {% endif%} + + +
+
+
+
+
+ +
+
+

Current status

+ Online +
+
+
+
+
+
+ +
+
+
+ +
+
+ +
+ +{%endblock%} + + + + + + + + + diff --git a/hosting/templates/hosting/virtual_machines.html b/hosting/templates/hosting/virtual_machines.html index 47eb23b1..fa673c99 100644 --- a/hosting/templates/hosting/virtual_machines.html +++ b/hosting/templates/hosting/virtual_machines.html @@ -19,11 +19,11 @@ {% for vm in vms %} - {{vm.id}} + {{vm.name}} {{vm.hosting_company_name}} {{vm.price}} CHF - + {% endfor %} diff --git a/hosting/urls.py b/hosting/urls.py index 31a3eaea..15aa4008 100644 --- a/hosting/urls.py +++ b/hosting/urls.py @@ -2,7 +2,8 @@ from django.conf.urls import url from .views import DjangoHostingView, RailsHostingView, PaymentVMView, \ NodeJSHostingView, LoginView, SignupView, IndexView, \ - OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView + OrdersHostingListView, OrdersHostingDetailView, VirtualMachinesPlanListView,\ + VirtualMachineDetailListView urlpatterns = [ url(r'index/?$', IndexView.as_view(), name='index'), @@ -13,6 +14,8 @@ urlpatterns = [ url(r'orders/?$', OrdersHostingListView.as_view(), name='orders'), url(r'orders/(?P\d+)/?$', OrdersHostingDetailView.as_view(), name='orders'), url(r'my-virtual-machines/?$', VirtualMachinesPlanListView.as_view(), name='virtual_machines'), + url(r'my-virtual-machines/(?P\d+)/?$', VirtualMachineDetailListView.as_view(), + name='virtual_machines'), url(r'login/?$', LoginView.as_view(), name='login'), url(r'signup/?$', SignupView.as_view(), name='signup'), url(r'^logout/?$', 'django.contrib.auth.views.logout', diff --git a/hosting/views.py b/hosting/views.py index 3494c983..b0e76028 100644 --- a/hosting/views.py +++ b/hosting/views.py @@ -247,3 +247,10 @@ class VirtualMachinesPlanListView(LoginRequiredMixin, ListView): user = self.request.user self.queryset = VirtualMachinePlan.objects.active(user) return super(VirtualMachinesPlanListView, self).get_queryset() + + +class VirtualMachineDetailListView(LoginRequiredMixin, DetailView): + template_name = "hosting/virtual_machine_detail.html" + login_url = reverse_lazy('hosting:login') + model = VirtualMachinePlan + context_object_name = "virtual_machine" From 6e8004bfd0c07b25138563d2693d2839b70797bc Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 4 May 2016 22:06:49 -0500 Subject: [PATCH 4/5] Changed hostingorder to m2m with vm plan model --- hosting/migrations/0013_auto_20160505_0302.py | 21 +++++++++++++++++++ hosting/models.py | 2 +- hosting/test_views.py | 0 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 hosting/migrations/0013_auto_20160505_0302.py create mode 100644 hosting/test_views.py diff --git a/hosting/migrations/0013_auto_20160505_0302.py b/hosting/migrations/0013_auto_20160505_0302.py new file mode 100644 index 00000000..ac4cd8e5 --- /dev/null +++ b/hosting/migrations/0013_auto_20160505_0302.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-05-05 03:02 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0012_auto_20160501_1850'), + ] + + operations = [ + migrations.AlterField( + model_name='hostingorder', + name='VMPlan', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hosting.VirtualMachinePlan'), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index ea650ebb..51930a12 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -110,7 +110,7 @@ class HostingOrder(models.Model): ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' - VMPlan = models.OneToOneField(VirtualMachinePlan) + VMPlan = models.ForeignKey(VirtualMachinePlan) customer = models.ForeignKey(StripeCustomer) billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) diff --git a/hosting/test_views.py b/hosting/test_views.py new file mode 100644 index 00000000..e69de29b From 342512036d77e20a2c98f7f9181eba58f48cbf69 Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 5 May 2016 01:03:35 -0500 Subject: [PATCH 5/5] Added DjangoHostingView test, Added RailsHostingView test, Added, NodeJSHostingView test, Changed VMPlan model, Fixed templates to support new relationship between orders and VMplans, Merged Calendar feature with Booking --- dynamicweb/settings/base.py | 4 +- hosting/managers.py | 5 +- hosting/migrations/0014_auto_20160505_0541.py | 21 ++++++ hosting/models.py | 6 +- hosting/templates/hosting/order_detail.html | 5 +- .../hosting/virtual_machine_detail.html | 2 +- hosting/test_views.py | 72 +++++++++++++++++++ membership/models.py | 2 +- utils/stripe_utils.py | 4 +- 9 files changed, 106 insertions(+), 15 deletions(-) create mode 100644 hosting/migrations/0014_auto_20160505_0541.py diff --git a/dynamicweb/settings/base.py b/dynamicweb/settings/base.py index 69e708d2..9d64dc60 100644 --- a/dynamicweb/settings/base.py +++ b/dynamicweb/settings/base.py @@ -446,8 +446,8 @@ AUTH_USER_MODEL = 'membership.CustomUser' # PAYMENT -STRIPE_API_PUBLIC_KEY = 'pk_test_ZRg6P8g5ybiHE6l2RW5pSaYV' # used in frontend to call from user browser -STRIPE_API_PRIVATE_KEY = 'sk_test_uIPMdgXoRGydrcD7fkwcn7dj' # used in backend payment +STRIPE_API_PUBLIC_KEY = 'pk_test_QqBZ50Am8KOxaAlOxbcm9Psl' # used in frontend to call from user browser +STRIPE_API_PRIVATE_KEY = 'sk_test_dqAmbKAij12QCGfkYZ3poGt2' # used in backend payment STRIPE_DESCRIPTION_ON_PAYMENT = "Payment for ungleich GmbH services" # EMAIL MESSAGES diff --git a/hosting/managers.py b/hosting/managers.py index 961b2c6f..c474d61e 100644 --- a/hosting/managers.py +++ b/hosting/managers.py @@ -4,5 +4,6 @@ from django.db import models class VMPlansManager(models.Manager): def active(self, user, **kwargs): - return self.select_related('hostingorder__customer__user').\ - filter(hostingorder__customer__user=user, hostingorder__approved=True, **kwargs) + return self.prefetch_related('hosting_orders__customer__user').\ + filter(hosting_orders__customer__user=user, hosting_orders__approved=True, **kwargs)\ + .distinct() diff --git a/hosting/migrations/0014_auto_20160505_0541.py b/hosting/migrations/0014_auto_20160505_0541.py new file mode 100644 index 00000000..532b25e8 --- /dev/null +++ b/hosting/migrations/0014_auto_20160505_0541.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.4 on 2016-05-05 05:41 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('hosting', '0013_auto_20160505_0302'), + ] + + operations = [ + migrations.AlterField( + model_name='hostingorder', + name='VMPlan', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='hosting_orders', to='hosting.VirtualMachinePlan'), + ), + ] diff --git a/hosting/models.py b/hosting/models.py index 51930a12..dd686312 100644 --- a/hosting/models.py +++ b/hosting/models.py @@ -95,10 +95,6 @@ class VirtualMachinePlan(models.Model): name = 'vm-%s' % self.id return name - @cached_property - def orders(self): - return [self.hostingorder] - @classmethod def create(cls, data, user): instance = cls.objects.create(**data) @@ -110,7 +106,7 @@ class HostingOrder(models.Model): ORDER_APPROVED_STATUS = 'Approved' ORDER_DECLINED_STATUS = 'Declined' - VMPlan = models.ForeignKey(VirtualMachinePlan) + VMPlan = models.ForeignKey(VirtualMachinePlan, related_name='hosting_orders') customer = models.ForeignKey(StripeCustomer) billing_address = models.ForeignKey(BillingAddress) created_at = models.DateTimeField(auto_now_add=True) diff --git a/hosting/templates/hosting/order_detail.html b/hosting/templates/hosting/order_detail.html index 4d5dcc18..74b8d5bb 100644 --- a/hosting/templates/hosting/order_detail.html +++ b/hosting/templates/hosting/order_detail.html @@ -23,7 +23,10 @@ Order Date:
{{object.created_at}}

Status:
- {{object.status}}

+ {{object.status}} +

diff --git a/hosting/templates/hosting/virtual_machine_detail.html b/hosting/templates/hosting/virtual_machine_detail.html index 87edd71f..e51e1d4e 100644 --- a/hosting/templates/hosting/virtual_machine_detail.html +++ b/hosting/templates/hosting/virtual_machine_detail.html @@ -100,7 +100,7 @@ - {% for order in virtual_machine.orders %} + {% for order in virtual_machine.hosting_orders.all %} {{order.id}} {{order.created_at}} diff --git a/hosting/test_views.py b/hosting/test_views.py index e69de29b..b2dfe9ae 100644 --- a/hosting/test_views.py +++ b/hosting/test_views.py @@ -0,0 +1,72 @@ +from django.test import TestCase +from django.core.urlresolvers import reverse +from django.core.urlresolvers import resolve +from .models import VirtualMachineType +from .views import DjangoHostingView, RailsHostingView, NodeJSHostingView + + +class ProcessVMSelectionTestMixin(object): + + def url_resolve_to_view_correctly(self): + found = resolve(self.url) + self.assertEqual(found.func.__name__, self.view.__name__) + + def test_get(self): + response = self.client.get(self.url) + self.assertEqual(response.status_code, 200) + self.assertEqual(self.view.get_context_data(), self.expected_context) + self.assertEqual(response.context['hosting'], self.expected_context['hosting']) + self.assertTemplateUsed(response, self.expected_template) + + def test_anonymous_post(self): + response = self.client.post(self.url) + self.assertRedirects(response, expected_url=reverse('hosting:login'), + status_code=302, target_status_code=200) + + +class DjangoHostingViewTest(TestCase, ProcessVMSelectionTestMixin): + + def setUp(self): + self.url = reverse('django.hosting') + self.view = DjangoHostingView() + self.expected_template = 'hosting/django.html' + self.expected_context = { + 'hosting': "django", + 'hosting_long': "Django", + 'domain': "django-hosting.ch", + 'google_analytics': "UA-62285904-6", + 'email': "info@django-hosting.ch", + 'vm_types': VirtualMachineType.get_serialized_vm_types(), + } + + +class RailsHostingViewTest(TestCase, ProcessVMSelectionTestMixin): + + def setUp(self): + self.url = reverse('rails.hosting') + self.view = RailsHostingView() + self.expected_template = 'hosting/rails.html' + self.expected_context = { + 'hosting': "rails", + 'hosting_long': "Ruby On Rails", + 'domain': "rails-hosting.ch", + 'google_analytics': "UA-62285904-5", + 'email': "info@rails-hosting.ch", + 'vm_types': VirtualMachineType.get_serialized_vm_types(), + } + + +class NodeJSHostingViewTest(TestCase, ProcessVMSelectionTestMixin): + + def setUp(self): + self.url = reverse('node.hosting') + self.view = NodeJSHostingView() + self.expected_template = 'hosting/nodejs.html' + self.expected_context = { + 'hosting': "nodejs", + 'hosting_long': "NodeJS", + 'domain': "node-hosting.ch", + 'google_analytics': "UA-62285904-7", + 'email': "info@node-hosting.ch", + 'vm_types': VirtualMachineType.get_serialized_vm_types(), + } diff --git a/membership/models.py b/membership/models.py index a607906c..75962660 100644 --- a/membership/models.py +++ b/membership/models.py @@ -131,7 +131,7 @@ class StripeCustomer(models.Model): Check if there is a registered stripe customer with that email or create a new one """ - + stripe_customer = None try: stripe_utils = StripeUtils() stripe_customer = cls.objects.get(user__email=email) diff --git a/utils/stripe_utils.py b/utils/stripe_utils.py index c0ea630b..ce14d417 100644 --- a/utils/stripe_utils.py +++ b/utils/stripe_utils.py @@ -52,8 +52,6 @@ def handleStripeError(f): return handleProblems - - class StripeUtils(object): CURRENCY = 'chf' INTERVAL = 'month' @@ -71,7 +69,7 @@ class StripeUtils(object): customer = stripe.Customer.retrieve(id) except stripe.InvalidRequestError: customer = self.create_customer(token, user.email) - user.stripecustomer.stripe_id = customer.get('id') + user.stripecustomer.stripe_id = customer.get('response_object').get('id') user.stripecustomer.save() return customer