Commit b732a62d authored by BERJON Matthieu's avatar BERJON Matthieu
Browse files

Rewriting of webapp creation view and template



I rewrote the the webapp creation view to satisfy new requirements. I
first changed the route to be coherent with the new routing system. I
changed the view name to be coherent with the new view naming (removing
the `View` as the end of the class name as it's obvious.

I rewrote completely the template for the webapp creation by adding two
panels, one for the basic and mandatory data and another one for the
advanced data.

I added a new button on the webapp list template that display a button
allowing only users in the `inria` group to create applications.

I created a template tag in order to be able to check if a user belongs
to a particular group in the template.

Signed-off-by: BERJON Matthieu's avatarMatthieu Berjon <matthieu.berjon@inria.fr>
parent bd17d158
......@@ -3,7 +3,15 @@ from django import forms
from django.contrib.auth.models import User
from django.core.validators import RegexValidator
from .models import AllgoUser, Job, JobQueue, Webapp, WebappVersion, Runner
from .models import (
AllgoUser,
DockerOs,
Job,
JobQueue,
Runner,
Webapp,
WebappVersion,
)
class UserForm(forms.ModelForm):
......@@ -115,6 +123,10 @@ class RunnerForm(forms.ModelForm):
class WebappForm(forms.ModelForm):
# Basic
name = forms.CharField(label="Application name", label_suffix="")
contact = forms.EmailField(label="Email contact", label_suffix="", required=False)
description = forms.CharField(widget=forms.Textarea, label="Description", label_suffix="")
private = forms.TypedChoiceField(
coerce=lambda x: x == 'True',
choices=((False, 'Public'), (True, 'Private')),
......@@ -123,6 +135,19 @@ class WebappForm(forms.ModelForm):
label_suffix='',
widget=forms.RadioSelect)
# Advanced
docker_os = forms.ModelChoiceField(
queryset=DockerOs.objects.all().distinct(),
initial=1,
label='Operating sytem',
label_suffix='')
memory_limit = forms.IntegerField(label="Memory limit", label_suffix='', required=False)
job_queue = forms.ModelChoiceField(
queryset=JobQueue.objects.all().distinct(),
initial=1,
label='Default queue type',
label_suffix='')
def __init__(self, *args, **kwargs):
super(WebappForm, self).__init__(*args, **kwargs)
......
from django import template
from django.contrib.auth.models import Group
register = template.Library()
@register.filter(name='has_group')
def has_group(user, group_name):
group = Group.objects.get(name=group_name)
return group in user.groups.all()
......@@ -18,6 +18,7 @@ urlpatterns = [
url(r'^apps/$', views.WebappList.as_view(), name='webapp_list'),
url(r'^apps/authors/(?P<username>[\w.@+-]+)/$', views.UserWebappList.as_view(), name='user_webapp_list'),
url(r'^apps/(?P<docker_name>[\w-]+)/update$', views.WebappUpdate.as_view(), name="webapp_update"),
url(r'^apps/create/$', views.WebappCreate.as_view(), name='webapp_creation'),
url(r'^app/(?P<docker_name>[\w-]+)/$', views.WebappDetail.as_view(), name='webapp_detail'),
url(r'^app/(?P<docker_name>[\w-]+)/create/job/$', views.WebappCreateJobView.as_view(), name='webapp_create_job'),
......@@ -28,8 +29,6 @@ urlpatterns = [
url(r'^jobs/(?P<pk>\d+)/download/all/$', views.JobFileDownloadAllView.as_view(), name='job_download_all'),
url(r'^settings/$', views.UserUpdateView.as_view(), name='user_settings'),
url(r'^settings/myapps/create$', views.WebappCreateView.as_view(), name='webapp_creation'),
url(r'^settings/myapps/create$', views.WebappCreateView.as_view(), name='webapp_creation'),
url(r'^settings/myapps/(?P<docker_name>[\w-]+)/sandbox/$', views.WebappSandboxCreationView.as_view(), name="webapp_sandbox_creation"),
url(r'^settings/myapps/(?P<docker_name>[\w-]+)/commit/$', views.WebappSandboxCreationView.as_view(), name="webapp_version_commit"),
url(r'^settings/security/$', views.UserSecurityDetailView.as_view(), name='security_settings'),
......
......@@ -465,6 +465,29 @@ class WebappUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
return super(WebappUpdate, self).form_invalid(form)
class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
model = Webapp
form_class = WebappForm
success_message = 'Webapp created successfully.'
template_name = 'webapp_creation_form.html'
def get_success_url(self):
return reverse('main:webapp_list')
def form_valid(self, form):
obj = form.save(commit=False)
obj.user_id = self.request.user.id
if not form.cleaned_data['contact']:
obj.contact = self.request.user.email
obj.sandbox_state = Webapp.STARTING
# Ensure that all specials characters are removed, spaces are replaced
# by hyphens and everything is lower-cased
obj.docker_name = slugify(form.cleaned_data['name'])
obj.save()
# set up the docker container for the app
Quota.objects.create(user=self.request.user, webapp=obj)
return super(WebappCreate, self).form_valid(form)
class JobList(LoginRequiredMixin, ListView):
......@@ -724,31 +747,6 @@ class UserSecuritySSHDeleteView(LoginRequiredMixin, RedirectView):
return reverse('main:security_settings')
class WebappCreateView(SuccessMessageMixin, LoginRequiredMixin, CreateView):
model = Webapp
form_class = WebappForm
success_message = 'Webapp created successfully.'
success_url = reverse_lazy('main:user_webapp_list')
template_name = 'webapp_creation_form.html'
def form_valid(self, form):
obj = form.save(commit=False)
# obj.webappversion_id = self.webapp.id
obj.user_id = self.request.user.id
if not form.cleaned_data['contact']:
obj.contact = self.request.user.email
obj.sandbox_state = Webapp.STARTING
# Ensure that all specials characters are removed, spaces are replaced
# by hyphens and everything is lower-cased
obj.docker_name = slugify(form.cleaned_data['name'])
obj.save()
# set up the docker container for the app
Quota.objects.create(user=self.request.user, webapp=obj)
# WebappVersion.objects.create(webapp=obj, state=WebappVersion.SANDBOX, published=False)
return super(WebappCreateView, self).form_valid(form)
class WebappSandboxCreationView(SuccessMessageMixin, LoginRequiredMixin, CreateView):
form_class = WebappSandboxForm
model = WebappVersion
......
......@@ -4,81 +4,108 @@
{% block title %}Create a webapp{% endblock %}
{% block breadcrumb %}
<li class="breadcrumb-item"><a href="{% url 'main:user_settings' %}">Settings</a></li>
<li class="breadcrumb-item"><a href="{% url 'main:user_webapp_list' %}">My webapps</a></li>
<li class="breadcrumb-item active" aria-current="page">Create a webapp</li>
<li class="breadcrumb-item"><a href="{% url 'main:user_webapp_list' user.get_username %}">My applications</a></li>
<li class="breadcrumb-item active" aria-current="page">Create</li>
{% endblock %}
{% block content %}
<div class="container">
<div class="allgo-page">
<div class="row">
{% include 'partials/_user_settings_menu.html' %}
<div class="col-10">
<h1 class="page-title">Create a new application</h1>
<form method="post">
{% csrf_token %}
<div class="form-group">
{{ form.name.label_tag }}
{% if form.name.errors %}
{{ form.name | attr:"placeholder:Name of the application" | add_class:"form-control is-invalid" }}
{% else %}
{{ form.name | attr:"placeholder:Name of the application" | add_class:"form-control" }}
{% endif %}
</div>
<ul class="nav nav-tabs">
<li class="nav-item"><a class="nav-link active" data-toggle="tab" href="#basic">Basic</a></li>
<li class="nav-item"><a class="nav-link" data-toggle="tab" href="#advanced">Advanced</a></li>
</ul>
<div class="form-group">
{{ form.description.label_tag }}
{{ form.description | attr:"placeholder:Description of the application" | add_class:"form-control" }}
</div>
<p>You can create a new application here by giving a name.</p>
<div class="form-group">
{{ form.contact.label_tag }}
{{ form.contact | attr:"placeholder:Email of the contact for the application" | add_class:"form-control" }}
</div>
<div class="form-group">
{{ form.job_queue.label_tag }}
<form method="post">
{% csrf_token %}
<select name="{{ form.job_queue.name }}" id="{{ form.job_queue.id_for_label }}" class="form-control">
{% for choice in form.job_queue.field.queryset %}
<option value="{{ choice.pk }}">{{ choice.name }}</option>
{% endfor %}
</select>
</div>
<div class="tab-content">
<div class="tab-pane active" id="basic">
<div class="form-row">
<div class="col">
<div class="form-group">
{{ form.name.label_tag }}
{% if form.name.errors %}
{{ form.name | attr:"placeholder:My great app" | add_class:"form-control is-invalid" }}
{% else %}
{{ form.name | attr:"placeholder:My great app" | add_class:"form-control" }}
{% endif %}
</div>
</div>
<div class="col">
<div class="form-group">
{{ form.contact.label_tag }}
{{ form.contact | attr:"placeholder:If different from you" | add_class:"form-control" }}
</div>
</div>
</div>
<div class="form-group">
{{ form.description.label_tag }}
{{ form.description | attr:"placeholder:Consider it as the app README file" | add_class:"form-control" }}
<small id="descriptionHelp" class="form-text text-muted"><a href="https://daringfireball.net/projects/markdown/syntax" title="Markdown syntax">Mardown syntax</a> is used for formatting.</small>
</div>
<div class="form-group">
{{ form.private.label_tag }}
<div class="btn-group btn-group-toggle" data-toggle="buttons">
{% for private_field in form.private %}
<label class="btn btn-secondary active">
<input type="{{ private_field.data.type }}" id="{{ private_field.id_for_label }}" value="{{ private_field.data.value }}" name="{{ private_field.data.name }}" {% if private_field.data.selected %}checked{% endif %}>{{ private_field.choice_label }}
</label>
{% endfor %}
</div>
</div>
<div class="form-group">
{{ form.docker_os.label_tag }}
</div>
<select name="{{ form.docker_os.name }}" id="{{ form.docker_os.id_for_label }}" class="form-control">
{% for choice in form.docker_os.field.queryset %}
<option value="{{ choice.pk }}">{{ choice.name }} {{ choice.version }}</option>
{% endfor %}
</select>
</div>
<div class="tab-pane" id="advanced">
<div class="form-row">
<div class="form-group col">
{{ form.docker_os.label_tag }}
<select name="{{ form.docker_os.name }}" id="{{ form.docker_os.id_for_label }}" class="form-control">
{% for choice in form.docker_os.field.queryset %}
<option value="{{ choice.pk }}">{{ choice.name }} {{ choice.version }}</option>
{% endfor %}
</select>
</div>
<div class="form-group col">
{{ form.memory_limit.label_tag }}
<div class="input-group">
{{ form.memory_limit | attr:"placeholder:Memory limit" | add_class:"form-control" }}
<div class="input-group-append">
<span class="input-group-text">GB</span>
</div>
</div>
</div>
</div>
<div class="form-group">
{{ form.memory_limit.label_tag }}
{{ form.memory_limit | attr:"placeholder:Memory limit" | add_class:"form-control" }}
</div>
<div class="form-row">
<div class="form-group col-md-6">
{{ form.job_queue.label_tag }}
<div class="form-group">
{{ form.private.label_tag }}
<div class="btn-group btn-group-toggle" data-toggle="buttons">
{% for private_field in form.private %}
<label class="btn btn-secondary active">
<input type="{{ private_field.data.type }}" id="{{ private_field.id_for_label }}" value="{{ private_field.data.value }}" name="{{ private_field.data.name }}" {% if private_field.data.selected %}checked{% endif %}>{{ private_field.choice_label }}
</label>
{% endfor %}
<select name="{{ form.job_queue.name }}" id="{{ form.job_queue.id_for_label }}" class="form-control">
{% for choice in form.job_queue.field.queryset %}
<option value="{{ choice.pk }}">{{ choice.name }}</option>
{% endfor %}
</select>
</div>
</div>
</div>
<input class="btn btn-primary" type="submit" value="Create webapp">
<input class="btn btn-primary float-right" type="submit" value="Create webapp">
</form>
</div>
</div>
</div>
</div>
......
{% extends "base.html" %}
{% load static %}
{% load static groups %}
{% block title %}Available apps{% endblock %}
......@@ -15,6 +15,17 @@
{% block content %}
<div class="container">
<div class="allgo-page">
<nav class="navbar">
{% if request.user|has_group:'inria' %}
<div class="dropdown show ml-auto mr-3 order-lg-last">
<a class="btn btn-light dropdown-toggle text-muted" href="#" id="dropdownMenuLink" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false"><i class="fas fa-cog"></i><span class="text-hide">Settings</span></a>
<div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
<a href="{% url 'main:webapp_creation' %}" class="dropdown-item" title="Create a new application">Create application</a>
</div>
</div>
{% endif %}
</nav>
{% for webapp in webapps %}
<div class="app-card">
<div class="row border-bottom">
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment