summaryrefslogtreecommitdiffstats
path: root/dashboard/src/account
diff options
context:
space:
mode:
Diffstat (limited to 'dashboard/src/account')
-rw-r--r--dashboard/src/account/__init__.py2
-rw-r--r--dashboard/src/account/admin.py5
-rw-r--r--dashboard/src/account/jira_util.py2
-rw-r--r--dashboard/src/account/migrations/0001_initial.py33
-rw-r--r--dashboard/src/account/migrations/0002_auto_20180110_1636.py33
-rw-r--r--dashboard/src/account/migrations/0002_lab_description.py19
-rw-r--r--dashboard/src/account/migrations/0003_auto_20180110_1639.py24
-rw-r--r--dashboard/src/account/migrations/0003_publicnetwork.py25
-rw-r--r--dashboard/src/account/migrations/__init__.py10
-rw-r--r--dashboard/src/account/models.py125
-rw-r--r--dashboard/src/account/tasks.py4
-rw-r--r--dashboard/src/account/tests/__init__.py2
-rw-r--r--dashboard/src/account/tests/test_general.py6
-rw-r--r--dashboard/src/account/urls.py29
-rw-r--r--dashboard/src/account/views.py145
15 files changed, 377 insertions, 87 deletions
diff --git a/dashboard/src/account/__init__.py b/dashboard/src/account/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/account/__init__.py
+++ b/dashboard/src/account/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/account/admin.py b/dashboard/src/account/admin.py
index 6f77122..b4c142c 100644
--- a/dashboard/src/account/admin.py
+++ b/dashboard/src/account/admin.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -10,7 +11,9 @@
from django.contrib import admin
-from account.models import UserProfile, Lab
+from account.models import UserProfile, Lab, VlanManager, PublicNetwork
admin.site.register(UserProfile)
admin.site.register(Lab)
+admin.site.register(VlanManager)
+admin.site.register(PublicNetwork)
diff --git a/dashboard/src/account/jira_util.py b/dashboard/src/account/jira_util.py
index fdb87f7..18b0e26 100644
--- a/dashboard/src/account/jira_util.py
+++ b/dashboard/src/account/jira_util.py
@@ -62,4 +62,4 @@ def get_jira(user):
'key_cert': key_cert
}
- return JIRA(server=settings.JIRA_URL, oauth=oauth_dict) \ No newline at end of file
+ return JIRA(server=settings.JIRA_URL, oauth=oauth_dict)
diff --git a/dashboard/src/account/migrations/0001_initial.py b/dashboard/src/account/migrations/0001_initial.py
index 591f702..c8b5bdc 100644
--- a/dashboard/src/account/migrations/0001_initial.py
+++ b/dashboard/src/account/migrations/0001_initial.py
@@ -1,6 +1,4 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2016-11-03 13:33
-from __future__ import unicode_literals
+# Generated by Django 2.1 on 2018-09-14 14:48
import account.models
from django.conf import settings
@@ -18,21 +16,50 @@ class Migration(migrations.Migration):
operations = [
migrations.CreateModel(
+ name='Lab',
+ fields=[
+ ('name', models.CharField(max_length=200, primary_key=True, serialize=False, unique=True)),
+ ('contact_email', models.EmailField(blank=True, max_length=200, null=True)),
+ ('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
+ ('status', models.IntegerField(default=0)),
+ ('location', models.TextField(default='unknown')),
+ ('api_token', models.CharField(max_length=50)),
+ ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+ ],
+ ),
+ migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('timezone', models.CharField(default='UTC', max_length=100)),
('ssh_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)),
('pgp_public_key', models.FileField(blank=True, null=True, upload_to=account.models.upload_to)),
+ ('email_addr', models.CharField(default='email@mail.com', max_length=300)),
('company', models.CharField(max_length=200)),
('oauth_token', models.CharField(max_length=1024)),
('oauth_secret', models.CharField(max_length=1024)),
('jira_url', models.CharField(default='', max_length=100)),
('full_name', models.CharField(default='', max_length=100)),
+ ('booking_privledge', models.BooleanField(default=False)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
+ migrations.CreateModel(
+ name='VlanManager',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlans', models.TextField()),
+ ('block_size', models.IntegerField()),
+ ('allow_overlapping', models.BooleanField()),
+ ('reserved_vlans', models.TextField()),
+ ],
+ ),
+ migrations.AddField(
+ model_name='lab',
+ name='vlan_manager',
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='account.VlanManager'),
+ ),
]
diff --git a/dashboard/src/account/migrations/0002_auto_20180110_1636.py b/dashboard/src/account/migrations/0002_auto_20180110_1636.py
deleted file mode 100644
index 6170acb..0000000
--- a/dashboard/src/account/migrations/0002_auto_20180110_1636.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:36
-from __future__ import unicode_literals
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('account', '0001_initial'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='Lab',
- fields=[
- ('id', models.CharField(max_length=200, primary_key=True, serialize=False)),
- ('name', models.CharField(max_length=200, unique=True)),
- ('contact_email', models.EmailField(blank=True, max_length=200, null=True)),
- ('contact_phone', models.CharField(blank=True, max_length=20, null=True)),
- ('lab_user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
- ],
- ),
- migrations.AddField(
- model_name='userprofile',
- name='email_addr',
- field=models.CharField(default='email@mail.com', max_length=300),
- ),
- ]
diff --git a/dashboard/src/account/migrations/0002_lab_description.py b/dashboard/src/account/migrations/0002_lab_description.py
new file mode 100644
index 0000000..445501a
--- /dev/null
+++ b/dashboard/src/account/migrations/0002_lab_description.py
@@ -0,0 +1,19 @@
+# Generated by Django 2.1 on 2018-09-14 20:22
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='lab',
+ name='description',
+ field=models.CharField(default='Lab description default', max_length=240),
+ preserve_default=False,
+ ),
+ ]
diff --git a/dashboard/src/account/migrations/0003_auto_20180110_1639.py b/dashboard/src/account/migrations/0003_auto_20180110_1639.py
deleted file mode 100644
index d0bc4d6..0000000
--- a/dashboard/src/account/migrations/0003_auto_20180110_1639.py
+++ /dev/null
@@ -1,24 +0,0 @@
-# -*- coding: utf-8 -*-
-# Generated by Django 1.10 on 2018-01-10 16:39
-from __future__ import unicode_literals
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('account', '0002_auto_20180110_1636'),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name='lab',
- name='id',
- ),
- migrations.AlterField(
- model_name='lab',
- name='name',
- field=models.CharField(max_length=200, primary_key=True, serialize=False, unique=True),
- ),
- ]
diff --git a/dashboard/src/account/migrations/0003_publicnetwork.py b/dashboard/src/account/migrations/0003_publicnetwork.py
new file mode 100644
index 0000000..71e5caa
--- /dev/null
+++ b/dashboard/src/account/migrations/0003_publicnetwork.py
@@ -0,0 +1,25 @@
+# Generated by Django 2.1 on 2018-09-26 14:41
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('account', '0002_lab_description'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='PublicNetwork',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('vlan', models.IntegerField()),
+ ('in_use', models.BooleanField(default=False)),
+ ('cidr', models.CharField(default='0.0.0.0/0', max_length=50)),
+ ('gateway', models.CharField(default='0.0.0.0', max_length=50)),
+ ('lab', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='account.Lab')),
+ ],
+ ),
+ ]
diff --git a/dashboard/src/account/migrations/__init__.py b/dashboard/src/account/migrations/__init__.py
index b5914ce..e69de29 100644
--- a/dashboard/src/account/migrations/__init__.py
+++ b/dashboard/src/account/migrations/__init__.py
@@ -1,10 +0,0 @@
-##############################################################################
-# Copyright (c) 2016 Max Breitenfeldt and others.
-#
-# All rights reserved. This program and the accompanying materials
-# are made available under the terms of the Apache License, Version 2.0
-# which accompanies this distribution, and is available at
-# http://www.apache.org/licenses/LICENSE-2.0
-##############################################################################
-
-
diff --git a/dashboard/src/account/models.py b/dashboard/src/account/models.py
index aad4c50..4fc7c40 100644
--- a/dashboard/src/account/models.py
+++ b/dashboard/src/account/models.py
@@ -10,11 +10,20 @@
from django.contrib.auth.models import User
from django.db import models
+import json
+import random
+
+
+class LabStatus(object):
+ UP = 0
+ TEMP_DOWN = 100
+ DOWN = 200
def upload_to(object, filename):
return object.user.username + '/' + filename
+
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
timezone = models.CharField(max_length=100, blank=False, default='UTC')
@@ -28,6 +37,7 @@ class UserProfile(models.Model):
jira_url = models.CharField(max_length=100, default='')
full_name = models.CharField(max_length=100, default='')
+ booking_privledge = models.BooleanField(default=False)
class Meta:
db_table = 'user_profile'
@@ -35,11 +45,126 @@ class UserProfile(models.Model):
def __str__(self):
return self.user.username
+
+class VlanManager(models.Model):
+ # list of length 4096 containing either 0 (not available) or 1 (available)
+ vlans = models.TextField()
+ block_size = models.IntegerField()
+ allow_overlapping = models.BooleanField()
+ # list of length 4096 containing either 0 (not rexerved) or 1 (reserved)
+ reserved_vlans = models.TextField()
+
+ def get_vlan(self, count=1):
+ allocated = []
+ vlans = json.loads(self.vlans)
+ for i in range(count):
+ new_vlan = vlans.index(1) # will throw if none available
+ vlans[new_vlan] = 0
+ allocated.append(new_vlan)
+ if count == 1:
+ return allocated[0]
+ return allocated
+
+ def get_public_vlan(self):
+ return PublicNetwork.objects.filter(lab=self.lab_set.first(), in_use=False).first()
+
+ def reserve_public_vlan(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=False)
+ net.in_use = True
+ net.save()
+
+ def release_public_vlan(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan, in_use=True)
+ net.in_use = False
+ net.save()
+
+ def public_vlan_is_available(self, vlan):
+ net = PublicNetwork.objects.get(lab=self.lab_set.first(), vlan=vlan)
+ return not net.in_use
+
+ def is_available(self, vlans):
+ """
+ 'vlans' is either a single vlan id integer or a list of integers
+ will return true (available) or false
+ """
+ if self.allow_overlapping:
+ return True
+
+ reserved = json.loads(self.reserved_vlans)
+ vlan_master_list = json.loads(self.vlans)
+ try:
+ iter(vlans)
+ except Exception:
+ vlans = [vlans]
+
+ for vlan in vlans:
+ if not vlan_master_list[vlan] or reserved[vlan]:
+ return False
+ return True
+
+ def release_vlans(self, vlans):
+ """
+ 'vlans' is either a single vlan id integer or a list of integers
+ will make the vlans available
+ doesnt return a value
+ """
+ my_vlans = json.loads(self.vlans)
+
+ try:
+ iter(vlans)
+ except Exception:
+ vlans = [vlans]
+
+ for vlan in vlans:
+ my_vlans[vlan] = 1
+ self.vlans = json.dumps(my_vlans)
+ self.save()
+
+ def reserve_vlans(self, vlans):
+ my_vlans = json.loads(self.vlans)
+
+ try:
+ iter(vlans)
+ except Exception:
+ vlans = [vlans]
+
+ vlans = set(vlans)
+
+ for vlan in vlans:
+ if my_vlans[vlan] == 0:
+ raise ValueError("vlan " + str(vlan) + " is not available")
+
+ my_vlans[vlan] = 0
+ self.vlans = json.dumps(my_vlans)
+ self.save()
+
+
class Lab(models.Model):
lab_user = models.OneToOneField(User, on_delete=models.CASCADE)
name = models.CharField(max_length=200, primary_key=True, unique=True, null=False, blank=False)
contact_email = models.EmailField(max_length=200, null=True, blank=True)
contact_phone = models.CharField(max_length=20, null=True, blank=True)
+ status = models.IntegerField(default=LabStatus.UP)
+ vlan_manager = models.ForeignKey(VlanManager, on_delete=models.CASCADE, null=True)
+ location = models.TextField(default="unknown")
+ api_token = models.CharField(max_length=50)
+ description = models.CharField(max_length=240)
+
+ @staticmethod
+ def make_api_token():
+ alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
+ key = ""
+ for i in range(45):
+ key += random.choice(alphabet)
+ return key
def __str__(self):
return self.name
+
+
+class PublicNetwork(models.Model):
+ vlan = models.IntegerField()
+ lab = models.ForeignKey(Lab, on_delete=models.CASCADE)
+ in_use = models.BooleanField(default=False)
+ cidr = models.CharField(max_length=50, default="0.0.0.0/0")
+ gateway = models.CharField(max_length=50, default="0.0.0.0")
diff --git a/dashboard/src/account/tasks.py b/dashboard/src/account/tasks.py
index bfb865d..fe51974 100644
--- a/dashboard/src/account/tasks.py
+++ b/dashboard/src/account/tasks.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -28,7 +29,6 @@ def sync_jira_accounts():
user.email = user_dict['emailAddress']
user.userprofile.url = user_dict['self']
user.userprofile.full_name = user_dict['displayName']
- print(user_dict)
user.userprofile.save()
- user.save() \ No newline at end of file
+ user.save()
diff --git a/dashboard/src/account/tests/__init__.py b/dashboard/src/account/tests/__init__.py
index b5914ce..b6fef6c 100644
--- a/dashboard/src/account/tests/__init__.py
+++ b/dashboard/src/account/tests/__init__.py
@@ -6,5 +6,3 @@
# which accompanies this distribution, and is available at
# http://www.apache.org/licenses/LICENSE-2.0
##############################################################################
-
-
diff --git a/dashboard/src/account/tests/test_general.py b/dashboard/src/account/tests/test_general.py
index e8f483b..3fb52b0 100644
--- a/dashboard/src/account/tests/test_general.py
+++ b/dashboard/src/account/tests/test_general.py
@@ -30,7 +30,7 @@ class AccountMiddlewareTestCase(TestCase):
The timezone should be UTC for anonymous users, for authenticated users it should be set
to user.userprofile.timezone
"""
- #default
+ # default
self.assertEqual(timezone.get_current_timezone_name(), 'UTC')
url = reverse('account:settings')
@@ -47,7 +47,7 @@ class AccountMiddlewareTestCase(TestCase):
self.user1profile.timezone = 'Etc/Greenwich'
self.user1profile.save()
self.client.get(url)
- self.assertEqual(timezone.get_current_timezone_name(), 'Etc/Greenwich')
+ self.assertEqual(timezone.get_current_timezone_name(), 'GMT')
# if there is no profile for a user, it should be created
user2 = User.objects.create(username='user2')
@@ -56,5 +56,3 @@ class AccountMiddlewareTestCase(TestCase):
self.client.login(username='user2', password='user2')
self.client.get(url)
self.assertTrue(user2.userprofile)
-
-
diff --git a/dashboard/src/account/urls.py b/dashboard/src/account/urls.py
index 3962a0c..8aad80c 100644
--- a/dashboard/src/account/urls.py
+++ b/dashboard/src/account/urls.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -24,13 +25,39 @@ Including another URLconf
2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls'))
"""
from django.conf.urls import url
+from django.urls import path
-from account.views import *
+from account.views import (
+ AccountSettingsView,
+ JiraAuthenticatedView,
+ JiraLoginView,
+ JiraLogoutView,
+ UserListView,
+ account_resource_view,
+ account_booking_view,
+ account_images_view,
+ account_configuration_view,
+ account_detail_view,
+ resource_delete_view,
+ booking_cancel_view,
+ image_delete_view,
+ configuration_delete_view
+)
+app_name = "account"
urlpatterns = [
url(r'^settings/', AccountSettingsView.as_view(), name='settings'),
url(r'^authenticated/$', JiraAuthenticatedView.as_view(), name='authenticated'),
url(r'^login/$', JiraLoginView.as_view(), name='login'),
url(r'^logout/$', JiraLogoutView.as_view(), name='logout'),
url(r'^users/$', UserListView.as_view(), name='users'),
+ url(r'^my/resources/$', account_resource_view, name="my-resources"),
+ path('my/resources/delete/<int:resource_id>', resource_delete_view),
+ url(r'^my/bookings/$', account_booking_view, name="my-bookings"),
+ path('my/bookings/cancel/<int:booking_id>', booking_cancel_view),
+ url(r'^my/images/$', account_images_view, name="my-images"),
+ path('my/images/delete/<int:image_id>', image_delete_view),
+ url(r'^my/configurations/$', account_configuration_view, name="my-configurations"),
+ path('my/configurations/delete/<int:config_id>', configuration_delete_view),
+ url(r'^my/$', account_detail_view, name="my-account"),
]
diff --git a/dashboard/src/account/views.py b/dashboard/src/account/views.py
index e6a0e5d..2b4eccb 100644
--- a/dashboard/src/account/views.py
+++ b/dashboard/src/account/views.py
@@ -1,5 +1,6 @@
##############################################################################
# Copyright (c) 2016 Max Breitenfeldt and others.
+# Copyright (c) 2018 Parker Berberian, Sawyer Bergeron, and others.
#
# All rights reserved. This program and the accompanying materials
# are made available under the terms of the Apache License, Version 2.0
@@ -13,20 +14,26 @@ import urllib
import oauth2 as oauth
from django.conf import settings
+from django.utils import timezone
from django.contrib import messages
from django.contrib.auth import logout, authenticate, login
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.urls import reverse
+from django.http import HttpResponse
+from django.shortcuts import get_object_or_404
from django.utils.decorators import method_decorator
from django.views.generic import RedirectView, TemplateView, UpdateView
+from django.shortcuts import render
from jira import JIRA
from rest_framework.authtoken.models import Token
from account.forms import AccountSettingsForm
from account.jira_util import SignatureMethod_RSA_SHA1
from account.models import UserProfile
+from booking.models import Booking
+from resource_inventory.models import GenericResourceBundle, ConfigBundle, Image, Host
@method_decorator(login_required, name='dispatch')
@@ -59,7 +66,7 @@ class JiraLoginView(RedirectView):
# Step 1. Get a request token from Jira.
try:
resp, content = client.request(settings.OAUTH_REQUEST_TOKEN_URL, "POST")
- except Exception as e:
+ except Exception:
messages.add_message(self.request, messages.ERROR,
'Error: Connection to Jira failed. Please contact an Administrator')
return '/'
@@ -72,8 +79,8 @@ class JiraLoginView(RedirectView):
self.request.session['request_token'] = dict(urllib.parse.parse_qsl(content.decode()))
# Step 3. Redirect the user to the authentication URL.
url = settings.OAUTH_AUTHORIZE_URL + '?oauth_token=' + \
- self.request.session['request_token']['oauth_token'] + \
- '&oauth_callback=' + settings.OAUTH_CALLBACK_URL
+ self.request.session['request_token']['oauth_token'] + \
+ '&oauth_callback=' + settings.OAUTH_CALLBACK_URL
return url
@@ -95,7 +102,7 @@ class JiraAuthenticatedView(RedirectView):
# Step 2. Request the authorized access token from Jira.
try:
resp, content = client.request(settings.OAUTH_ACCESS_TOKEN_URL, "POST")
- except Exception as e:
+ except Exception:
messages.add_message(self.request, messages.ERROR,
'Error: Connection to Jira failed. Please contact an Administrator')
return '/'
@@ -153,3 +160,133 @@ class UserListView(TemplateView):
context = super(UserListView, self).get_context_data(**kwargs)
context.update({'title': "Dashboard Users", 'users': users})
return context
+
+
+def account_detail_view(request):
+ template = "account/details.html"
+ return render(request, template)
+
+
+def account_resource_view(request):
+ """
+ gathers a users genericResoureBundles and
+ turns them into displayable objects
+ """
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/resource_list.html"
+ resources = GenericResourceBundle.objects.filter(
+ owner=request.user).prefetch_related("configbundle_set")
+ mapping = {}
+ resource_list = []
+ booking_mapping = {}
+ for grb in resources:
+ resource_list.append(grb)
+ mapping[grb.id] = [{"id": x.id, "name": x.name} for x in grb.configbundle_set.all()]
+ if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
+ booking_mapping[grb.id] = "true"
+ context = {
+ "resources": resource_list,
+ "grb_mapping": mapping,
+ "booking_mapping": booking_mapping,
+ "title": "My Resources"
+ }
+ return render(request, template, context=context)
+
+
+def account_booking_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/booking_list.html"
+ bookings = list(Booking.objects.filter(owner=request.user, end__gt=timezone.now()).order_by("-start"))
+ my_old_bookings = Booking.objects.filter(owner=request.user, end__lt=timezone.now()).order_by("-start")
+ collab_old_bookings = request.user.collaborators.filter(end__lt=timezone.now()).order_by("-start")
+ expired_bookings = list(my_old_bookings.union(collab_old_bookings))
+ collab_bookings = list(request.user.collaborators.filter(end__gt=timezone.now()).order_by("-start"))
+ context = {
+ "title": "My Bookings",
+ "bookings": bookings,
+ "collab_bookings": collab_bookings,
+ "expired_bookings": expired_bookings
+ }
+ return render(request, template, context=context)
+
+
+def account_configuration_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/configuration_list.html"
+ configs = list(ConfigBundle.objects.filter(owner=request.user))
+ context = {"title": "Configuration List", "configurations": configs}
+ return render(request, template, context=context)
+
+
+def account_images_view(request):
+ if not request.user.is_authenticated:
+ return render(request, "dashboard/login.html", {'title': 'Authentication Required'})
+ template = "account/image_list.html"
+ my_images = Image.objects.filter(owner=request.user)
+ public_images = Image.objects.filter(public=True)
+ used_images = {}
+ for image in my_images:
+ if Host.objects.filter(booked=True, config__image=image).exists():
+ used_images[image.id] = "true"
+ context = {
+ "title": "Images",
+ "images": my_images,
+ "public_images": public_images,
+ "used_images": used_images
+ }
+ return render(request, template, context=context)
+
+
+def resource_delete_view(request, resource_id=None):
+ if not request.user.is_authenticated:
+ return HttpResponse('no') # 403?
+ grb = get_object_or_404(GenericResourceBundle, pk=resource_id)
+ if not request.user.id == grb.owner.id:
+ return HttpResponse('no') # 403?
+ if Booking.objects.filter(resource__template=grb, end__gt=timezone.now()).exists():
+ return HttpResponse('no') # 403?
+ grb.delete()
+ return HttpResponse('')
+
+
+def configuration_delete_view(request, config_id=None):
+ if not request.user.is_authenticated:
+ return HttpResponse('no') # 403?
+ config = get_object_or_404(ConfigBundle, pk=config_id)
+ if not request.user.id == config.owner.id:
+ return HttpResponse('no') # 403?
+ if Booking.objects.filter(config_bundle=config, end__gt=timezone.now()).exists():
+ return HttpResponse('no')
+ config.delete()
+ return HttpResponse('')
+
+
+def booking_cancel_view(request, booking_id=None):
+ if not request.user.is_authenticated:
+ return HttpResponse('no') # 403?
+ booking = get_object_or_404(Booking, pk=booking_id)
+ if not request.user.id == booking.owner.id:
+ return HttpResponse('no') # 403?
+
+ if booking.end < timezone.now(): # booking already over
+ return HttpResponse('')
+
+ booking.end = timezone.now()
+ booking.save()
+ return HttpResponse('')
+
+
+def image_delete_view(request, image_id=None):
+ if not request.user.is_authenticated:
+ return HttpResponse('no') # 403?
+ image = get_object_or_404(Image, pk=image_id)
+ if image.public or image.owner.id != request.user.id:
+ return HttpResponse('no') # 403?
+ # check if used in booking
+ if Host.objects.filter(booked=True, config__image=image).exists():
+ return HttpResponse('no') # 403?
+ image.delete()
+ return HttpResponse('')