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

Docstring update

Major update of the docstring at every levels (module, classes, methods).
I reordered as well the library calls, deleted a duplicate and replaced the
call to the `User` model by its Django counterpart.
parent 185cb4cf
# -*- coding: utf-8 -*-
"""Main url module
This module handles all URLs related to the `main` django application.
Below a short description of the different type of URLs available for
front-end.
/apps list all the public apps
/apps/author/<username> list all public apps of a particular owner
/apps/tag/<tag> list all apps according to a particular tag
/app[s]/<docker_name> app details
/runners/ list all runners of a given user
/runners/create create a runner
/runners/<runner_id>/ details of a runner
/runners/<runner_id>/update update a given runner
/runners/<runner_id>/delete delete a given runner
/profile profile of user (name, first name, email)
/profile/password update password
/profile/security SSH key and token
/jobs/ list all jobs of the user
/jobs/<job_id>/ details of a given job
/jobs/<job_id>/delete delete the job
/jobs/<job_id>/issue report an issue
/jobs/<job_id>/relaunch re-run a given job
Todo:
- make the app details available both at /app/<docker_name> (historic
version) and at /apps/<docker_name>/ (more coherent with the overall
system.
See also:
This url file depends on the main `urls.py` file located in the `config`
folder at the root of the Django application.
"""
# Third party import
from django.conf.urls import url, include
# Local import
from . import views
app_name = 'main'
......@@ -30,27 +71,3 @@ urlpatterns = [
url(r'^runners/(?P<pk>\d+)/update/$', views.RunnerUpdate.as_view(), name='runner_update'),
url(r'^runners/(?P<pk>\d+)/delete/$', views.RunnerDelete.as_view(), name='runner_delete'),
]
"""
# Apps
/apps list all the public apps
/apps/author/<username> list all public apps of a particular owner
/apps/tag/<tag> list all apps according to a particular tag
/app[s]/<docker_name> app details
/runners/ list all runners of a given user
/runners/create create a runner
/runners/<runner_id>/ details of a runner
/runners/<runner_id>/update update a given runner
/runners/<runner_id>/delete delete a given runner
/profile profile of user (name, first name, email)
/profile/password update password
/profile/security SSH key and token
/jobs/ list all jobs of the user
/jobs/<job_id>/ details of a given job
/jobs/<job_id>/delete delete the job
/jobs/<job_id>/issue report an issue
/jobs/<job_id>/relaunch re-run a given job
"""
import io
import io
# -*- coding: utf-8 -*-
"""Main view module
This module handles most of the front-end for the Allgo system. You'll find
all the logic (controller) in an MVC pattern.
Attributes:
log: module level variable to save information as a log data.
"""
# Python standard libraries
import glob
import io
import json
import logging
import os
import zipfile
# Third party imports
import config.env
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.forms import PasswordChangeForm
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.contrib.messages.views import SuccessMessageMixin
from django.core.urlresolvers import reverse
from django.http import HttpResponse
......@@ -19,10 +31,28 @@ from django.urls import reverse, reverse_lazy
from django.utils.crypto import get_random_string
from django.utils.text import slugify
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import ListView, DetailView, UpdateView, CreateView, DeleteView, RedirectView, View
from django.views.generic import (
CreateView,
DeleteView,
DetailView,
ListView,
RedirectView,
TemplateView,
UpdateView,
View,
)
import redis
# Local imports
from .mixins import GroupRequiredMixin
from .models import Webapp, Job, User, AllgoUser, WebappVersion, Runner, Quota
from .models import (
AllgoUser,
Job,
Quota,
Runner,
Webapp,
WebappVersion,
)
from .forms import (
UserForm,
HomeSignupForm,
......@@ -36,6 +66,7 @@ from .forms import (
from .helpers import get_ssh_data, read_runner_logs, upload_data
# Start logger
log = logging.getLogger('allgo')
......@@ -59,16 +90,22 @@ def index(request):
# WEBAPPS
# -----------------------------------------------------------------------------
class WebappList(ListView):
"""
Display the paginated list of available webapps.
""" Display a paginated list of available webapps.
**Context**
The webapps are filtered from the most recent to the oldest and no private
apps are displayed.
``Webapp``
An instance of :model:`main.Webapp`
Attributes:
model: Webapp model is used.
context_object_name: the name used in the template to display each
variable.
paginate_by: the number of occurences per page
template_name: name of the template loaded with this view.
queryset: a specific queryset designed to filter the data.
**Template:**
:template:`templates/webapp_list.html`
Todo:
- the number of occurences per page could be loaded from the config
file.
"""
model = Webapp
......@@ -79,8 +116,15 @@ class WebappList(ListView):
class WebappDetail(DetailView):
"""
Display the details of an app based on its docker name
"""Returns data of a specific webapp
The details of an app are returned based on its docker name and not its ID.
Attributes:
model: model used for the page.
template_name: template filename to return.
context_object_name: name of the variable used in the template to
display the data.
"""
model = Webapp
......@@ -88,14 +132,18 @@ class WebappDetail(DetailView):
context_object_name = 'webapp'
def get_object(self):
"""recover data according the docker_name rather than the ID."""
"""Recover data according the docker_name rather than the ID."""
data = self.kwargs.get('docker_name', None)
queryset = get_object_or_404(Webapp, docker_name=data)
return queryset
def get_context_data(self, **kwargs):
"""Recover the README file if exist and convert it from markdown to
HTML
"""Recover the README file and convert it
If a README file exists in the database (setup at `True`) we'll get it
and pass it to template. The template we'll load it and pass it to a
converter to transform the content from Markdown to plain HTML.
"""
context = super(WebappDetail, self).get_context_data(**kwargs)
......@@ -117,6 +165,19 @@ class WebappDetail(DetailView):
class UserWebappList(ListView):
"""List of user's webapp
Returns all the webapps owned by a specific user. Only the user can its
apps.
Attributes:
model: database model
context_object_name: variable name used in the template to display the
data.
paginate_by: number of occurences by page.
template_name: template filename.
"""
model = Webapp
context_object_name = 'webapps'
paginate_by = 10
......@@ -130,19 +191,30 @@ class UserWebappList(ListView):
class WebappUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Form to update the webapp data
Attributes:
form_class: form object.
template_name: template filename.
success_message: message when the form is properly submitted.
"""
form_class = UserWebappForm
template_name = 'webapp_update.html'
success_message = 'Your app has been successfully updated.'
def get_success_url(self):
"""If successful redirect to the same page"""
return reverse('main:webapp_update', args=(self.object.docker_name,))
def get_object(self):
"""Returns the object according to its docker name or a 404 error"""
data = self.kwargs.get('docker_name', None)
queryset = get_object_or_404(Webapp, docker_name=data, user_id=self.request.user.id)
return queryset
def form_valid(self, form):
"""Save data coming from the form in the database """
obj = form.save(commit=False)
try:
user = User.objects.get(username=form.cleaned_data['owner'])
......@@ -159,6 +231,16 @@ class WebappUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin, CreateView):
"""Create a new webapp
Attributes:
model: model to use in this class.
form_class: form object passed to the template.
success_message: successfull message sent to the template
template_name: template filename.
group_required: groups that user must belong to.
"""
model = Webapp
form_class = WebappForm
success_message = 'Webapp created successfully.'
......@@ -166,9 +248,11 @@ class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin,
group_required = ['inria', ]
def get_success_url(self):
"""If successful redirect to the webapp list page"""
return reverse('main:user_webapp_list', args=(self.request.user,))
def form_valid(self, form):
"""Save data coming from the form in the database """
obj = form.save(commit=False)
obj.user_id = self.request.user.id
if not form.cleaned_data['contact']:
......@@ -185,15 +269,24 @@ class WebappCreate(SuccessMessageMixin, LoginRequiredMixin, GroupRequiredMixin,
class WebappSandboxCreation(SuccessMessageMixin, LoginRequiredMixin, CreateView):
"""Create a new sandbox for a given application
Attributes:
form_class: form object to pass on the template.
model: model to use in this class
template_name: template filename
"""
form_class = WebappSandboxForm
model = WebappVersion
template_name = 'webapp_sandbox_add.html'
def get_success_url(self):
"""If successful redirect to the webapp list page"""
return reverse('main:user_webapp_list', args=(self.request.user,))
def form_valid(self, form):
"""Save data coming from the form in the database """
webapp = Webapp.objects.get(docker_name=self.kwargs['docker_name'])
obj = form.save(commit=False)
......@@ -206,11 +299,19 @@ class WebappSandboxCreation(SuccessMessageMixin, LoginRequiredMixin, CreateView)
return super(WebappSandboxCreation, self).form_valid(form)
def get_object(self):
"""Returns the object according to its docker name or a 404 error"""
data = self.kwargs.get('docker_name', None)
queryset = get_object_or_404(Webapp, docker_name=data)
return queryset
def get_context_data(self, **kwargs):
"""Recover data to pass on to the template context
In order to give the user a feedback regarding the way to push its image
to the registry, we need to pass both the webapp `docker_name` and the
`registry` URL.
"""
context = super(WebappSandboxCreation, self).get_context_data(**kwargs)
context['webapp'] = Webapp.objects.get(docker_name=self.kwargs['docker_name'])
context['registry'] = settings.DJANGO_REGISTRY
......@@ -321,20 +422,34 @@ class JobFileDownloadAllView(LoginRequiredMixin, View):
# PROFILE
# -----------------------------------------------------------------------------
class UserUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Update the user profile
Attributes:
form_class: form object to pass on the template.
template_name: template filename
success_message: successfull message sent to the template
"""
form_class = UserForm
template_name = "user_update.html"
success_message = 'Profile updated successfully.'
def get_success_url(self):
"""If successful redirect to the user page"""
return reverse('main:user_detail')
def get_object(self):
# Only get the User record for the user making the request
"""Only get the User record for the user making the request"""
return User.objects.get(username=self.request.user.username)
def get_context_data(self, **kwargs):
"""Recover data to pass on to the template
In order to display specific data, I process the SSH key to get its
fingerprint and comment. Both the SSH key, fingerprint, comment and
token to context template.
"""
queryset = AllgoUser.objects.get(user_id=self.request.user.id)
key = queryset.sshkey
token = queryset.token
......@@ -349,47 +464,68 @@ class UserUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
class UserToken(LoginRequiredMixin, RedirectView):
"""Regenerate the user token"""
def dispatch(self, request, *args, **kwargs):
"""Generate the token and save it into the database"""
queryset = AllgoUser.objects.get(user_id=self.request.user.id)
queryset.token = get_random_string(length=32)
queryset.save()
return super(UserToken, self).dispatch(request, *args, **kwargs)
def get_redirect_url(self, *args, **kwargs):
"""Redirect the user to the user page and display a successful message"""
messages.add_message(self.request, messages.SUCCESS, 'Token generated successfully.')
return reverse('main:user_detail')
class UserSSHAdd(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Save a SSH key to the database.
Attributes:
form_class: form object to pass on the template.
template_name: template filename
success_message: successfull message sent to the template
"""
form_class = SSHForm
template_name = 'user_ssh_add.html'
success_message = 'SSH key added successfully.'
def get_success_url(self):
"""If successful redirect to the user page"""
return reverse('main:user_detail')
def get_object(self):
"""Only get the User record for the user making the request"""
return AllgoUser.objects.get(user_id=self.request.user.id)
class UserSSHDelete(LoginRequiredMixin, RedirectView):
"""Delete the user SSH key"""
def dispatch(self, request, *args, **kwargs):
"""Generate an empty SSH key and save it into the database"""
queryset = AllgoUser.objects.get(user_id=request.user.id)
queryset.sshkey = ''
queryset.save()
return super(UserSSHDelete, self).dispatch(request, *args, **kwargs)
def get_redirect_url(self, *args, **kwargs):
"""If successful redirect to the user page"""
return reverse('main:user_detail')
class UserPasswordUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
""" Update the password.
"""Update the user's password.
We reuse the Django password form system in order to keep something robust
even if it dedicates a specific view for it.
Attributes:
success_url: URL or handle where the user will be redirected.
form_class: form object to pass on the template.
template_name: template filename
success_message: successfull message sent to the template
"""
success_url = reverse_lazy('main:user_detail')
form_class = PasswordChangeForm
......@@ -397,14 +533,26 @@ class UserPasswordUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
success_message = 'Password updated successfully.'
def get_object(self, queryset=None):
"""Return the user data
Todo:
- Not sure the relevance of getting this information to the template
"""
return self.request.user
def get_form_kwargs(self):
"""Return the arguments related to the user"""
kwargs = super(UserPasswordUpdate, self).get_form_kwargs()
kwargs['user'] = kwargs.pop('instance')
return kwargs
def dispatch(self, request, *args, **kwargs):
"""
Todo:
- I'm not sure why I wrote that and why it is useful in the present
case. It needs to be investigated.
"""
return super(UserPasswordUpdate, self) \
.dispatch(request, *args, **kwargs)
......@@ -412,8 +560,18 @@ class UserPasswordUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
# -----------------------------------------------------------------------------
class JobList(LoginRequiredMixin, ListView):
"""
Display the list of jobs for a given identified user
"""Display the list of jobs for a given identified user
Attributes:
model: model used in the view.
context_object_name: the name used in the template to display each
variable.
paginate_by: the number of occurences per page
template_name: name of the template loaded with this view.
redirect_field_name: None
Todo:
- Check the relevance of `redirect_field_name` and delete it if necessary
"""
model = Job
context_object_name = 'job_list'
......@@ -459,6 +617,16 @@ class JobDetail(LoginRequiredMixin, DetailView):
class JobCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
"""Save a job into the database
Attributes:
model: model used in the view.
form_class: form object to pass on the template.
success_url: URL or handle where the user will be redirected.
success_message: successfull message sent to the template
template_name: name of the template loaded with this view.
"""
model = Job
form_class = JobForm
success_message = 'Job created successfully.'
......@@ -466,6 +634,7 @@ class JobCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
template_name = 'job_add_update.html'
def form_valid(self, form):
"""Save data coming from the form in the database """
webapp = Webapp.objects.get(docker_name=self.kwargs['docker_name'])
obj = form.save(commit=False)
......@@ -476,12 +645,14 @@ class JobCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
obj.webapp_id = webapp.id
obj.save()
# Upload files if there are any
if self.request.FILES:
latest_job = Job.objects.filter(user_id=self.request.user.id).order_by('-id')[0]
upload_data(self.request.FILES.getlist('files'), latest_job.id)
return super(JobCreate, self).form_valid(form)
def get_context_data(self, **kwargs):
"""Pass on the docker name to the template"""
queryset = Webapp.objects.get(docker_name=self.kwargs['docker_name'])
kwargs['webapp_name'] = queryset.name
return super(JobCreate, self).get_context_data(**kwargs)
......@@ -489,8 +660,17 @@ class JobCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
# RUNNERS
# -----------------------------------------------------------------------------
class RunnerList(LoginRequiredMixin, ListView):
"""List all runners of a given user
Attributes:
model: model used in the view.
context_object_name: the name used in the template to display each
variable.
paginate_by: the number of occurences per page
template_name: name of the template loaded with this view.
"""
model = Runner
context_object_name = 'runner_list'
paginate_by = 10
......@@ -502,6 +682,15 @@ class RunnerList(LoginRequiredMixin, ListView):
class RunnerCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
"""Create a runner and save it into the database
Attributes:
model: model used in the view.
form_class: form object to pass on the template.
success_message: successfull message sent to the template
template_name: name of the template loaded with this view.
"""
model = Runner
form_class = RunnerForm
success_message = 'Runner saved successfully.'
......@@ -509,12 +698,23 @@ class RunnerCreate(SuccessMessageMixin, LoginRequiredMixin, CreateView):
template_name = 'runner_add_update.html'
def get_form_kwargs(self):
"""Pass on the request data onto the template"""
kwargs = super(RunnerCreate, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
class RunnerUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
"""Update a runner and save it into the database
Attributes:
model: model used in the view.
form_class: form object to pass on the template.
success_message: successfull message sent to the template
success_url: URL or handle where the user will be redirected.
template_name: name of the template loaded with this view.
"""
model = Runner
form_class = RunnerForm
success_message = 'Runner updated successfully.'
......@@ -522,12 +722,22 @@ class RunnerUpdate(SuccessMessageMixin, LoginRequiredMixin, UpdateView):
template_name = 'runner_add_update.html'
def get_form_kwargs(self):
"""Pass on the request data onto the template"""
kwargs = super(RunnerUpdate, self).get_form_kwargs()
kwargs['request'] = self.request
return kwargs
class RunnerDelete(SuccessMessageMixin, LoginRequiredMixin, DeleteView):
"""Delete a runner
Attributes:
model: model used in the view.
success_message: successfull message sent to the template
success_url: URL or handle where the user will be redirected.
template_name: name of the template loaded with this view.
"""
model = Runner
success_message = 'Runner successfully deleted.'
success_url = reverse_lazy('main:runner_list')
......
Markdown is supported
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