diff --git a/.gitignore b/.gitignore
index 67e3e930ee6d44df6cf4e61e5dfe717a798ec856..111d38d59810b993f6a71dc2c7b947e0939a8e1e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,7 +30,6 @@ env-grady/
 env/
 scripts/
 *.csv
-*.json
 .importer*
 
 # operation system
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index d323ad32eb7ee7ed5a2099aa039ec659274a0e86..8fb02b54899d0a570bca8622b67179bed8073272 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -34,6 +34,12 @@ test_pylint:
                 - cd backend/
                 - pylint core || exit 0
 
+test_prospector:
+        <<: *test_definition
+        script:
+                - cd backend/
+                - prospector --uses django || exit 0
+
 
 .staging_template: &staging_definition
         stage: staging
diff --git a/backend/.pylintrc b/backend/.pylintrc
index 5f460322f42aa18322284f4aedbba8be75317b40..dc0fdb2dc4da190c837ce47ca197e7bd4ffe95ab 100644
--- a/backend/.pylintrc
+++ b/backend/.pylintrc
@@ -2,7 +2,7 @@
 
 # Add files or directories to the blacklist. They should be base names, not
 # paths.
-ignore=CVS, migrations, static, env, docs, manage.py, tests
+ignore=CVS, migrations, static, env, docs, manage.py
 
 # Add files or directories matching the regex patterns to the blacklist. The
 # regex matches against base names, not paths.
@@ -13,7 +13,7 @@ ignore-patterns=
 #init-hook=
 
 # Use multiple processes to speed up Pylint.
-jobs=2
+jobs=4
 
 # Pickle collected data for later comparisons.
 persistent=yes
@@ -26,6 +26,8 @@ suggestion-mode=yes
 # active Python interpreter and may run arbitrary code.
 unsafe-load-any-extension=no
 
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
 load-plugins=pylint_django
 
 [MESSAGES CONTROL]
diff --git a/backend/Makefile b/backend/Makefile
index 1fbb4228f68f15ca35cebd67c73522c1b30ef5bd..64d359b2954d6172665aec7772875bbe9e4e9b98 100644
--- a/backend/Makefile
+++ b/backend/Makefile
@@ -31,12 +31,12 @@ install:
 	yarn
 
 test:
-	python manage.py run test
+	python manage.py test
 
 coverage:
 	coverage run manage.py test
 	coverage report
 
 db:
-	docker run -rm --name $(DB_NAME) -p 5432:5432 postgres:9.5
+	docker run --rm --name $(DB_NAME) -p 5432:5432 postgres:9.5
 
diff --git a/backend/core/admin.py b/backend/core/admin.py
index 9598b953d8bcb6c9cd0045ee101dbe133c295da7..7de2ec69610f75e0a7ee00849683bac6e5fc3e2f 100644
--- a/backend/core/admin.py
+++ b/backend/core/admin.py
@@ -1,85 +1,19 @@
-from django import forms
 from django.contrib import admin
-from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
-from django.contrib.auth.forms import ReadOnlyPasswordHashField
+from django.contrib.auth.models import Group
 
 from core.models import (Feedback, Reviewer, Student, Submission,
-                         SubmissionType, Test, Tutor, UserAccount)
+                         SubmissionType, Test, Tutor, UserAccount, ExamType)
 
-
-class UserCreationForm(forms.ModelForm):
-    """A form for creating new users. Includes all the required
-    fields, plus a repeated password."""
-    password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
-    password2 = forms.CharField(
-        label='Password confirmation', widget=forms.PasswordInput)
-
-    class Meta:
-        model = UserAccount
-        fields = ()
-
-    def clean_password2(self):
-        # Check that the two password entries match
-        password1 = self.cleaned_data.get("password1")
-        password2 = self.cleaned_data.get("password2")
-        if password1 and password2 and password1 != password2:
-            raise forms.ValidationError("Passwords don't match")
-        return password2
-
-    def save(self, commit=True):
-        # Save the provided password in hashed format
-        user = super(UserCreationForm, self).save(commit=False)
-        user.set_password(self.cleaned_data["password1"])
-        if commit:
-            user.save()
-        return user
-
-
-class UserChangeForm(forms.ModelForm):
-    """A form for updating users. Includes all the fields on
-    the user, but replaces the password field with admin's
-    password hash display field.
-    """
-    password = ReadOnlyPasswordHashField()
-
-    class Meta:
-        model = UserAccount
-        fields = ('password', 'is_active', 'is_admin')
-
-    def clean_password(self):
-        return self.initial["password"]
-
-
-class UserAdmin(BaseUserAdmin):
-    # The forms to add and change user instances
-    form = UserChangeForm
-    add_form = UserCreationForm
-
-    # The fields to be used in displaying the User model.
-    # These override the definitions on the base UserAdmin
-    # that reference specific fields on auth.User.
-    list_display = ('username', 'is_admin',)
-    list_filter = ('is_admin',)
-    fieldsets = (
-        (None, {'fields': ('password',)}),
-        ('Permissions', {'fields': ('is_admin',)}),
-    )
-    # add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
-    # overrides get_fieldsets to use this attribute when creating a user.
-    add_fieldsets = (
-        (None, {
-            'classes': ('wide',),
-            'fields': ('password1', 'password2')}
-         ),
-    )
-    filter_horizontal = ()
-
-
-admin.site.register(UserAccount, UserAdmin)
+# Stuff we needwant
+admin.site.register(UserAccount)
 admin.site.register(SubmissionType)
 admin.site.register(Feedback)
 admin.site.register(Test)
+admin.site.register(ExamType)
 admin.site.register(Submission)
 admin.site.register(Reviewer)
 admin.site.register(Student)
 admin.site.register(Tutor)
+
+# ... and stuff we don't needwant
+admin.site.unregister(Group)
diff --git a/backend/core/migrations/0001_initial.py b/backend/core/migrations/0001_initial.py
index 8f531591b207dcbb13a24fdc092743b32da8f340..39b10c6dff91fd6ba136f6c7dd74e2870b375bb2 100644
--- a/backend/core/migrations/0001_initial.py
+++ b/backend/core/migrations/0001_initial.py
@@ -2,10 +2,11 @@
 # Generated by Django 1.11.7 on 2017-11-04 19:10
 from __future__ import unicode_literals
 
-import core.models
+import django.db.models.deletion
 from django.conf import settings
 from django.db import migrations, models
-import django.db.models.deletion
+
+import core.models
 
 
 class Migration(migrations.Migration):
diff --git a/backend/core/migrations/0002_auto_20171110_1612.py b/backend/core/migrations/0002_auto_20171110_1612.py
new file mode 100644
index 0000000000000000000000000000000000000000..51ea77b9d911e7133d6fd9f8f4d1c6d52d9b90d5
--- /dev/null
+++ b/backend/core/migrations/0002_auto_20171110_1612.py
@@ -0,0 +1,79 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.7 on 2017-11-10 16:12
+from __future__ import unicode_literals
+
+import django.contrib.auth.models
+import django.contrib.auth.validators
+from django.db import migrations, models
+import django.utils.timezone
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('auth', '0008_alter_user_username_max_length'),
+        ('core', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.AlterModelOptions(
+            name='useraccount',
+            options={'verbose_name': 'user', 'verbose_name_plural': 'users'},
+        ),
+        migrations.AlterModelManagers(
+            name='useraccount',
+            managers=[
+                ('objects', django.contrib.auth.models.UserManager()),
+            ],
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='date_joined',
+            field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined'),
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='email',
+            field=models.EmailField(blank=True, max_length=254, verbose_name='email address'),
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='first_name',
+            field=models.CharField(blank=True, max_length=30, verbose_name='first name'),
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='groups',
+            field=models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups'),
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='last_name',
+            field=models.CharField(blank=True, max_length=30, verbose_name='last name'),
+        ),
+        migrations.AddField(
+            model_name='useraccount',
+            name='user_permissions',
+            field=models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions'),
+        ),
+        migrations.AlterField(
+            model_name='useraccount',
+            name='is_active',
+            field=models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active'),
+        ),
+        migrations.AlterField(
+            model_name='useraccount',
+            name='is_staff',
+            field=models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status'),
+        ),
+        migrations.AlterField(
+            model_name='useraccount',
+            name='is_superuser',
+            field=models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status'),
+        ),
+        migrations.AlterField(
+            model_name='useraccount',
+            name='username',
+            field=models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username'),
+        ),
+    ]
diff --git a/backend/core/models.py b/backend/core/models.py
index 01218655b51f2038ba486d39aeac3ff74614ade8..32189b7e4f1c9f88c313e528b86583a216112b5b 100644
--- a/backend/core/models.py
+++ b/backend/core/models.py
@@ -10,8 +10,8 @@ from collections import OrderedDict
 from random import randrange, sample
 from string import ascii_lowercase
 
-from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
 from django.contrib.auth import get_user_model
+from django.contrib.auth.models import AbstractUser
 from django.db import models
 from django.db.models import Value as V
 from django.db.models import (BooleanField, Case, Count, F, IntegerField, Q,
@@ -150,64 +150,16 @@ class SubmissionType(models.Model):
             ).order_by('name')
 
 
-class UserAccountManager(BaseUserManager):
-
-    def create_user(self, password=None, **kwargs):
-        if not kwargs.get('username'):
-            raise ValueError('Users must have a valid username.')
-
-        account = self.model(
-            username=kwargs.get('username')
-        )
-
-        account.set_password(password)
-        account.save()
-
-        return account
-
-    def create_superuser(self, password, **kwargs):
-        account = self.create_user(password, **kwargs)
-
-        account.is_admin = True
-        account.is_staff = True
-        account.is_superuser = True
-        account.save()
-
-        return account
-
-
-class UserAccount(AbstractBaseUser):
+class UserAccount(AbstractUser):
     """
     An abstract base class implementing a fully featured User model with
     admin-compliant permissions.
 
     Username and password are required. Other fields are optional.
     """
-    objects = UserAccountManager()
-
-    username = models.CharField(
-        max_length=150,
-        unique=True,
-        error_messages={
-            'unique': "A user with that username already exists.",
-        },
-    )
 
     fullname = models.CharField('full name', max_length=70, blank=True)
-
-    is_staff = models.BooleanField('staff status', default=False)
     is_admin = models.BooleanField(default=False)
-    is_superuser = models.BooleanField(default=False)
-
-    is_active = models.BooleanField('active', default=True)
-
-    USERNAME_FIELD = 'username'
-
-    def has_perm(self, *args):
-        return self.is_superuser
-
-    def has_module_perms(self, *args):
-        return self.is_superuser
 
     def get_associated_user(self):
         """ Returns the user type that is associated with this user obj """
@@ -216,12 +168,6 @@ class UserAccount(AbstractBaseUser):
             (hasattr(self, 'reviewer') and self.reviewer) or \
             (hasattr(self, 'tutor') and self.tutor)
 
-    def get_short_name(self):
-        return self.username
-
-    def get_username(self):
-        return self.username
-
 
 class Tutor(models.Model):
     user = models.OneToOneField(
@@ -535,10 +481,10 @@ class Feedback(models.Model):
         ACCEPTED,
     ) = range(4)  # this order matters
     STATUS = (
-        (EDITABLE,      'editable'),
-        (OPEN,          'request reassignment'),
-        (NEEDS_REVIEW,  'request review'),
-        (ACCEPTED,      'accepted'),
+        (EDITABLE, 'editable'),
+        (OPEN, 'request reassignment'),
+        (NEEDS_REVIEW, 'request review'),
+        (ACCEPTED, 'accepted'),
     )
     status = models.IntegerField(
         choices=STATUS,
@@ -554,11 +500,11 @@ class Feedback(models.Model):
         MANUAL,
     ) = range(5)
     ORIGIN = (
-        (WAS_EMPTY,         'was empty'),
+        (WAS_EMPTY, 'was empty'),
         (FAILED_UNIT_TESTS, 'passed unittests'),
-        (DID_NOT_COMPILE,   'did not compile'),
-        (COULD_NOT_LINK,    'could not link'),
-        (MANUAL,            'created by a human. yak!'),
+        (DID_NOT_COMPILE, 'did not compile'),
+        (COULD_NOT_LINK, 'could not link'),
+        (MANUAL, 'created by a human. yak!'),
     )
     origin = models.IntegerField(
         choices=ORIGIN,
diff --git a/backend/core/permissions.py b/backend/core/permissions.py
index 78a03977d1d5f4fedcab1a3677b05ed643cb954f..3418f6dd13ad69513594f22ef6612261614d72fd 100644
--- a/backend/core/permissions.py
+++ b/backend/core/permissions.py
@@ -1,18 +1,9 @@
 from rest_framework import permissions
-from core.custom_annotations import in_groups
 
-from core.models import Student, Submission, Feedback
+from core.models import Student
 
 
-class StudentRequestOwnData(permissions.BasePermission):
-    def has_object_permission(self, request, view, obj):
-        if in_groups(request.user, ['Students']):
-            student = request.user.student
-            if isinstance(obj, Student):
-                return student == obj
-            elif isinstance(obj, Submission):
-                return student == obj.student
-            elif isinstance(obj, Feedback):
-                return student == obj.of_submission.student
-
-        return False
+class IsStudent(permissions.BasePermission):
+    def has_permission(self, request, view):
+        user = request.user
+        return user.is_authenticated() and isinstance(user.get_associated_user(), Student)
diff --git a/backend/core/serializers.py b/backend/core/serializers.py
index 76801c874c65ae70b245d3f22c5b20fe8b5381da..a5d7967f6d46e68132d215b3f577abec331c15ab 100644
--- a/backend/core/serializers.py
+++ b/backend/core/serializers.py
@@ -1,25 +1,37 @@
-from django.contrib.auth.models import User
-
 from rest_framework import serializers
-from core.models import Student, Submission, Feedback
+
+from core.models import ExamType, Feedback, Student, Submission
+
+class ExamSerializer(serializers.ModelSerializer):
+
+    class Meta:
+        model = ExamType
+        fields = ('module_reference', 'total_score',
+                  'pass_score', 'pass_only',)
 
 
 class FeedbackSerializer(serializers.ModelSerializer):
+
     class Meta:
         model = Feedback
         fields = ('text', 'score')
 
 
 class SubmissionSerializer(serializers.ModelSerializer):
-    feedback = FeedbackSerializer()
+    feedback = serializers.ReadOnlyField(source='feedback.text')
+    score = serializers.ReadOnlyField(source='feedback.score')
+    type = serializers.ReadOnlyField(source='type.name')
+    full_score = serializers.ReadOnlyField(source='type.full_score')
 
     class Meta:
         model = Submission
-        fields = ('seen_by_student', 'text', 'type', 'student', 'feedback')
+        fields = ('type', 'text', 'feedback', 'score', 'full_score')
 
 
 class StudentSerializer(serializers.ModelSerializer):
+    name = serializers.ReadOnlyField(source='user.fullname')
     user = serializers.ReadOnlyField(source='user.username')
+    exam = ExamSerializer()
     submissions = SubmissionSerializer(many=True)
 
     class Meta:
diff --git a/backend/core/tests.py b/backend/core/tests.py
index d9db4611624af0d5fa2d8253bf2e121ca19da2a1..f3290fbe0132092e58ed50fa7db0e04406b9b5e3 100644
--- a/backend/core/tests.py
+++ b/backend/core/tests.py
@@ -1,6 +1,7 @@
 from django.test import TestCase
 
-from core.models import Submission, SubmissionType, Feedback, Student, Tutor, Reviewer
+from core.models import (Feedback, Reviewer, Student, Submission,
+                         SubmissionType, Tutor)
 from util.importer import GradyUserFactory
 
 
@@ -53,6 +54,7 @@ class FactoryTestCase(TestCase):
     def test_can_create_student(self):
         self.assertIn(self.factory.make_student(), Student.objects.all())
 
+
 class AccountsTestCase(TestCase):
 
     factory = GradyUserFactory()
diff --git a/backend/core/urls.py b/backend/core/urls.py
index 035834813224f49645ba55c1a4321003deea8709..539e3183125cbdc9b55d83b91c356ed9757c5212 100644
--- a/backend/core/urls.py
+++ b/backend/core/urls.py
@@ -1,10 +1,9 @@
 from django.conf.urls import url
 from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from rest_framework_jwt.views import obtain_jwt_token
 
 from core import views
 
-
-
 urlpatterns = [
     url(r'^$', views.IndexView.as_view(), name='index'),
     url(r'^login/$', views.Login.as_view(), name='login'),
@@ -27,8 +26,10 @@ urlpatterns = [
     url(r'^csv/$', views.export_csv, name='export'),
 
     url(r'^api/student/$', views.StudentApiView.as_view()),
-    url(r'^api/submission/$', views.SubmissionApiView.as_view()),
-    url(r'^api/feedback/$', views.FeedbackApiView.as_view()),
+    url(r'^api/student/submission/(?P<pk>[0-9]+)$', views.SubmissionApiView.as_view()),
+    url(r'^api/student/submission/(?P<pk>[0-9]+)/feedback/$', views.FeedbackApiView.as_view()),
+
+    url(r'^api-token-auth/', obtain_jwt_token)
 ]
 
 urlpatterns += staticfiles_urlpatterns()
diff --git a/backend/core/views/api.py b/backend/core/views/api.py
index bf64153f9560136adb8da2879a3af3500ec748ca..d0d45630fc5e653657f3302e5639cda68ed0d096 100644
--- a/backend/core/views/api.py
+++ b/backend/core/views/api.py
@@ -1,28 +1,33 @@
+import logging
 
-from core.models import Student, Submission, Feedback
-from core.serializers import SubmissionSerializer, StudentSerializer, FeedbackSerializer
 from rest_framework.generics import RetrieveAPIView
 
+from core.permissions import IsStudent
+from core.serializers import (FeedbackSerializer, StudentSerializer,
+                              SubmissionSerializer)
+
+log = logging.getLogger(__name__)
 
 class StudentApiView(RetrieveAPIView):
+    permission_classes = (IsStudent,)
+
     def get_object(self):
+        log.debug("Serializing student of user '%s'", self.request.user.username)
         return self.request.user.student
     serializer_class = StudentSerializer
 
 
 class SubmissionApiView(RetrieveAPIView):
-    def get_object(self):
+    permission_classes = (IsStudent,)
+
+    def get_queryset(self):
         return self.request.user.student.submissions
     serializer_class = SubmissionSerializer
 
 
 class FeedbackApiView(RetrieveAPIView):
+    permission_classes = (IsStudent,)
+
     def get_queryset(self):
         return [submission.feedback for submission in self.request.user.submissions]
     serializer_class = FeedbackSerializer
-
-
-class StudentPageView(RetrieveAPIView):
-    queryset = Student.objects.all()
-
-
diff --git a/backend/grady/settings/default.py b/backend/grady/settings/default.py
index 839caa89fb8fb1fae739864256e29b9fd18fbbda..86e0c94956f52ceb7b459c8d8e015d73c4825a6c 100644
--- a/backend/grady/settings/default.py
+++ b/backend/grady/settings/default.py
@@ -10,6 +10,7 @@ For the full list of settings and their values, see
 https://docs.djangoproject.com/en/1.10/ref/settings/
 """
 
+import datetime
 import os
 
 from django.contrib.messages import constants as messages
@@ -143,3 +144,67 @@ AUTH_PASSWORD_VALIDATORS = []
 CORS_ORIGIN_WHITELIST = (
     'localhost:8080'
 )
+
+REST_FRAMEWORK = {
+    'DEFAULT_PERMISSION_CLASSES': (
+        'rest_framework.permissions.IsAuthenticated',
+    ),
+    'DEFAULT_AUTHENTICATION_CLASSES': (
+        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
+        'rest_framework.authentication.SessionAuthentication',
+        'rest_framework.authentication.BasicAuthentication',
+    ),
+}
+
+JWT_AUTH = {
+    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=600),
+}
+
+LOGGING = {
+    "version": 1,
+    "disable_existing_loggers": False,
+    "formatters": {
+        'django.server': {
+            'datefmt': '%d/%b/%Y %H:%M:%S',
+            'format': '[%(asctime)s] %(levelname)-10s %(name)-20s %(message)s',
+        },
+        'core': {
+            'datefmt': '%d/%b/%Y %H:%M:%S',
+            'format': '[%(asctime)s] %(levelname)-10s %(name)-20s "%(message)s"',
+        },
+    },
+    'filters': {
+        'require_debug_true': {
+            '()': 'django.utils.log.RequireDebugTrue',
+        },
+    },
+    'handlers': {
+        'console': {
+            'level': 'DEBUG',
+            'filters': ['require_debug_true'],
+            'class': 'logging.StreamHandler',
+            'formatter': 'core'
+        },
+        'django': {
+            'level': 'INFO',
+            'class': 'logging.StreamHandler',
+            'formatter': 'django.server'
+        },
+        'mail_admins': { # TODO: configuration
+            'level': 'ERROR',
+            'class': 'django.utils.log.AdminEmailHandler',
+        }
+    },
+    'loggers': {
+        'django': {
+            'handlers': ['django'],
+        },
+        'django.request': {
+            'handlers': ['django'],
+        },
+        'core': {
+            'handlers': ['console', 'mail_admins'],
+            'level': 'DEBUG',
+        }
+    }
+}
diff --git a/backend/requirements.txt b/backend/requirements.txt
index b5f170750297f2f79f68dd732a8371f1cbc4ff72..d035c1effbd4e161ec2d139f5398ae54073ebee1 100644
--- a/backend/requirements.txt
+++ b/backend/requirements.txt
@@ -1,10 +1,11 @@
 Django~=1.11.3
 django-extensions~=1.7.7
 djangorestframework~=3.6.3
+djangorestframework-jwt~=1.11.0
 django_compressor~=2.1.1
 gunicorn~=19.7.0
 psycopg2~=2.7.1
 xlrd~=1.0.0
 pytest-cov~=2.5.1
-pylint~=1.7.4
+prospector~=0.12.7
 django-cors-headers~=2.1.0
diff --git a/backend/util/importer.py b/backend/util/importer.py
index 5c792deb3ea9c515b8b3e3e5a2ed5aca265db2d8..299f4ae547c1b8e1a8a9dcd2568bf3ca29d0b407 100644
--- a/backend/util/importer.py
+++ b/backend/util/importer.py
@@ -8,8 +8,9 @@ from typing import Callable
 
 import util.convert
 import util.processing
-from core.models import (ExamType, Feedback, Student, Tutor, Reviewer, Submission,
-                         SubmissionType, Test, UserAccount as User)
+from core.models import UserAccount as User
+from core.models import (ExamType, Feedback, Reviewer, Student, Submission,
+                         SubmissionType, Test, Tutor)
 from util.messages import info, warn
 from util.processing import EmptyTest
 
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..f265370c031533dae786d607882f9c39a36a2aca
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,96 @@
+{
+  "name": "frontend",
+  "version": "1.0.0",
+  "description": "Vue.js frontend for Grady",
+  "author": "robinwilliam.hundt <robinwilliam.hundt@stud.uni-goettingen.de>",
+  "private": true,
+  "scripts": {
+    "dev": "node build/dev-server.js",
+    "start": "npm run dev",
+    "build": "node build/build.js",
+    "unit": "cross-env BABEL_ENV=test karma start test/unit/karma.conf.js --single-run",
+    "test": "karma start test/unit/karma.conf.js",
+    "lint": "eslint --ext .js,.vue src test/unit/specs"
+  },
+  "dependencies": {
+    "axios": "^0.17.0",
+    "bootstrap": "4.0.0-beta.2",
+    "bootstrap-vue": "^1.0.0",
+    "vue": "^2.5.2",
+    "vue-router": "^3.0.1",
+    "vuex": "^3.0.1"
+  },
+  "devDependencies": {
+    "autoprefixer": "^7.1.2",
+    "babel-core": "^6.22.1",
+    "babel-eslint": "^7.1.1",
+    "babel-loader": "^7.1.1",
+    "babel-plugin-istanbul": "^4.1.1",
+    "babel-plugin-transform-runtime": "^6.22.0",
+    "babel-preset-env": "^1.3.2",
+    "babel-preset-stage-2": "^6.22.0",
+    "babel-register": "^6.22.0",
+    "chai": "^4.1.2",
+    "chalk": "^2.0.1",
+    "connect-history-api-fallback": "^1.3.0",
+    "copy-webpack-plugin": "^4.0.1",
+    "cross-env": "^5.0.1",
+    "css-loader": "^0.28.0",
+    "es6-promise": "^4.1.1",
+    "eslint": "^3.19.0",
+    "eslint-config-standard": "^10.2.1",
+    "eslint-friendly-formatter": "^3.0.0",
+    "eslint-loader": "^1.7.1",
+    "eslint-plugin-html": "^3.0.0",
+    "eslint-plugin-import": "^2.7.0",
+    "eslint-plugin-node": "^5.2.0",
+    "eslint-plugin-promise": "^3.4.0",
+    "eslint-plugin-standard": "^3.0.1",
+    "eventsource-polyfill": "^0.9.6",
+    "express": "^4.14.1",
+    "extract-text-webpack-plugin": "^3.0.0",
+    "file-loader": "^1.1.4",
+    "friendly-errors-webpack-plugin": "^1.6.1",
+    "html-webpack-plugin": "^2.30.1",
+    "http-proxy-middleware": "^0.17.3",
+    "inject-loader": "^3.0.0",
+    "karma": "^1.4.1",
+    "karma-coverage": "^1.1.1",
+    "karma-mocha": "^1.3.0",
+    "karma-phantomjs-launcher": "^1.0.2",
+    "karma-phantomjs-shim": "^1.4.0",
+    "karma-sinon-chai": "^1.3.1",
+    "karma-sourcemap-loader": "^0.3.7",
+    "karma-spec-reporter": "0.0.31",
+    "karma-webpack": "^2.0.2",
+    "mocha": "^3.2.0",
+    "opn": "^5.1.0",
+    "optimize-css-assets-webpack-plugin": "^3.2.0",
+    "ora": "^1.2.0",
+    "phantomjs-prebuilt": "^2.1.14",
+    "portfinder": "^1.0.13",
+    "rimraf": "^2.6.0",
+    "semver": "^5.3.0",
+    "shelljs": "^0.7.6",
+    "sinon": "^4.0.0",
+    "sinon-chai": "^2.8.0",
+    "url-loader": "^0.5.8",
+    "vue-loader": "^13.3.0",
+    "vue-style-loader": "^3.0.1",
+    "vue-template-compiler": "^2.5.2",
+    "webpack": "^3.6.0",
+    "webpack-bundle-analyzer": "^2.9.0",
+    "webpack-dev-middleware": "^1.12.0",
+    "webpack-hot-middleware": "^2.18.2",
+    "webpack-merge": "^4.1.0"
+  },
+  "engines": {
+    "node": ">= 4.0.0",
+    "npm": ">= 3.0.0"
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not ie <= 8"
+  ]
+}
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 20f09c82af20a098c0be0fb79cafbf8247199cc9..11aee7fdf9c9e58f33b670323ac9e2620793f1b1 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -1,23 +1,20 @@
 <template>
   <div id="app">
-    <img src="./assets/logo.png">
     <router-view/>
   </div>
 </template>
 
 <script>
   export default {
-    name: 'app'
+    name: 'app',
+    components: {
+
+    }
 }
 </script>
 
 <style>
 #app {
-  font-family: 'Avenir', Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-  margin-top: 60px;
+
 }
 </style>
diff --git a/backend/core/static/res/brand.png b/frontend/src/assets/brand.png
similarity index 100%
rename from backend/core/static/res/brand.png
rename to frontend/src/assets/brand.png
diff --git a/frontend/src/components/HelloWorld.vue b/frontend/src/components/HelloWorld.vue
deleted file mode 100644
index 7a4afec5d46561236a571fef2ce5996b47a0d192..0000000000000000000000000000000000000000
--- a/frontend/src/components/HelloWorld.vue
+++ /dev/null
@@ -1,14 +0,0 @@
-<template>
-  <span>{{msg}}</span>
-</template>
-
-<script>
-export default {
-  name: 'HelloWorld',
-  data () {
-    return {
-      msg: 'Welcome to Your Vue.js App'
-    }
-  }
-}
-</script>
diff --git a/frontend/src/components/Login.vue b/frontend/src/components/Login.vue
new file mode 100644
index 0000000000000000000000000000000000000000..0ea4acc6f699578d07e13ef4ecfbcd6ae19eb4d5
--- /dev/null
+++ b/frontend/src/components/Login.vue
@@ -0,0 +1,62 @@
+<template>
+  <div class="mx-auto col-md-4 col-xl-2" id="login">
+    <img src="../assets/brand.png"/>
+    <h2>Log in</h2>
+    <p>Log in to your account to grade stuff!</p>
+    <div class="aler alert-danger" v-if="error">
+      <p>{{ error }}</p>
+    </div>
+    <div class="form-group">
+      <input
+        type="text"
+        class="form-control"
+        placeholder="Enter your username"
+        v-model="credentials.username"
+      />
+    </div>
+    <div class="form-group" @keyup.enter="submit()">
+      <input
+        type="password"
+        class="form-control"
+        placeholder="Enter your password"
+        v-model="credentials.password"
+      />
+    </div>
+    <button class="btn btn-primary" @click="submit()">Access</button>
+  </div>
+</template>
+
+
+<script>
+  export default {
+    name: 'grady-login',
+    data () {
+      return {
+        credentials: {
+          username: '',
+          password: ''
+        },
+        error: ''
+      }
+    },
+    methods: {
+      submit () {
+        const credentials = {
+          username: this.credentials.username,
+          password: this.credentials.password
+        }
+        this.$store.dispatch('getToken', credentials).then(response => {
+          this.$router.push('/student/')
+        })
+      }
+    }
+  }
+</script>
+
+
+<style scoped>
+  #login {
+    text-align: center;
+    margin-top: 10%;
+  }
+</style>
diff --git a/frontend/src/components/student/Base.vue b/frontend/src/components/student/Base.vue
deleted file mode 100644
index 346f7ee00047d77a67a8a0a7b45cd62f64fe524b..0000000000000000000000000000000000000000
--- a/frontend/src/components/student/Base.vue
+++ /dev/null
@@ -1,13 +0,0 @@
-<template>
-
-</template>
-
-
-<script>
-
-</script>
-
-
-<style>
-
-</style>
diff --git a/frontend/src/components/student/ExamInformation.vue b/frontend/src/components/student/ExamInformation.vue
new file mode 100644
index 0000000000000000000000000000000000000000..817cd58c1e42da874890066be1d87409c4d5e4cd
--- /dev/null
+++ b/frontend/src/components/student/ExamInformation.vue
@@ -0,0 +1,29 @@
+<template>
+  <table class="table table-info rounded">
+    <tbody>
+      <tr>
+        <th>Modul</th>
+        <td>{{ exam.module_reference }}</td>
+      </tr>
+      <tr v-if="!exam.pass_only">
+        <th>Pass score</th>
+        <td>{{ exam.pass_score }}</td>
+      </tr>
+      <tr v-else>
+        <th>Pass only!</th>
+      </tr>
+      <tr>
+        <th>Total score</th>
+        <td>{{ exam.total_score }}</td>
+      </tr>
+    </tbody>
+  </table>
+</template>
+
+
+<script>
+  export default {
+    name: 'exam-information',
+    props: ['exam']
+  }
+</script>
diff --git a/frontend/src/components/student/StudentNav.vue b/frontend/src/components/student/StudentNav.vue
new file mode 100644
index 0000000000000000000000000000000000000000..6613a097b7116f3935298a0d4db14d817fe5cabd
--- /dev/null
+++ b/frontend/src/components/student/StudentNav.vue
@@ -0,0 +1,38 @@
+<template>
+  <b-navbar toggleable="md" type="light" variant="light">
+    <b-navbar-toggle target="nav_collapse"></b-navbar-toggle>
+
+    <b-navbar-brand>
+      <img src="../../assets/brand.png" width="30" class="d-inline-block align-top">
+      Grady
+    </b-navbar-brand>
+
+    <b-collapse is-nav id="nav_collapse">
+
+      <b-navbar-nav id="nav-left">
+        <b-nav-item class="active" href="#">Results</b-nav-item>
+        <b-nav-item href="#">Statistics</b-nav-item>
+      </b-navbar-nav>
+
+      <!-- Right aligned nav items -->
+      <b-navbar-nav class="ml-auto">
+        <b-nav-item>{{ this.$store.state.username }}</b-nav-item>
+        <router-link to="/">
+          <b-button class="btn-dark" @click="logout()" >Signout</b-button>
+        </router-link>
+      </b-navbar-nav>
+    </b-collapse>
+  </b-navbar>
+</template>
+
+
+<script>
+  export default {
+    name: 'grady-nav',
+    methods: {
+      logout () {
+        this.$store.dispatch('logout')
+      }
+    }
+  }
+</script>
diff --git a/frontend/src/components/student/StudentPage.vue b/frontend/src/components/student/StudentPage.vue
new file mode 100644
index 0000000000000000000000000000000000000000..b1e9249692fde918bbbfb6f48106301ed3fb62ef
--- /dev/null
+++ b/frontend/src/components/student/StudentPage.vue
@@ -0,0 +1,57 @@
+<template>
+  <div>
+    <grady-nav></grady-nav>
+    <div class="container-fluid">
+      <div class="row justify-content-center my-3">
+        <div class="col-md-3">
+          <h2 class="my-5">Exam Overview</h2>
+          <exam-information v-if="doneLoading" :exam="exam"></exam-information>
+        </div>
+        <div class="col-md-6 offset-md-1" v-if="doneLoading">
+          <h2 class="my-5">Submissions of {{ this.studentData.name }}</h2>
+          <submission-list :submissions="submissions"></submission-list>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+
+<script>
+  import ax from '@/store/api'
+  import GradyNav from './StudentNav.vue'
+  import SubmissionList from './SubmissionList.vue'
+  import ExamInformation from './ExamInformation.vue'
+
+  export default {
+    components: {
+      ExamInformation,
+      SubmissionList,
+      GradyNav},
+    name: 'student-page',
+    data () {
+      return {
+        studentData: {},
+        doneLoading: false
+      }
+    },
+    created: function () {
+      this.doneLoading = false
+      ax.get('api/student/').then(response => {
+        this.studentData = response.data
+        this.doneLoading = true
+      })
+    },
+    computed: {
+      submissions () {
+        return this.studentData.submissions
+      },
+      exam () {
+        return this.studentData.exam
+      }
+    }
+  }
+</script>
+
+<style scoped>
+</style>
diff --git a/frontend/src/components/student/SubmissionDetail.vue b/frontend/src/components/student/SubmissionDetail.vue
deleted file mode 100644
index 9a7264b44cb929ad25331ddf73d47ce87fedce95..0000000000000000000000000000000000000000
--- a/frontend/src/components/student/SubmissionDetail.vue
+++ /dev/null
@@ -1,12 +0,0 @@
-<template>
-
-</template>
-
-
-<script>
-  export default {
-    computed: {
-
-    }
-  }
-</script>
diff --git a/frontend/src/components/student/SubmissionList.vue b/frontend/src/components/student/SubmissionList.vue
index f4cf766fd04a72f6b2565a5a11f7d872eb802c34..c39e745fc152cca06a50428cc0f9fe595b858230 100644
--- a/frontend/src/components/student/SubmissionList.vue
+++ b/frontend/src/components/student/SubmissionList.vue
@@ -1,33 +1,35 @@
 <template>
-  <ul>
-    <li v-for="sub in submissions">
-      <span>{{sub.type}}</span>
-      <span>{{sub.text}}</span>
-      <span>{{sub.feedback}}</span>
-    </li>
-  </ul>
+  <div class="row my-2 justify-content-center">
+    <b-table hover :items="submissions" :fields="fields"></b-table>
+    <div class="alert alert-info">
+      You reached <b>{{ sumScore }}</b> of <b>{{ sumFullScore }}</b> possible points( {{ pointRatio }}% ).
+    </div>
+  </div>
 </template>
 
+
 <script>
   export default {
-    data: function () {
+    name: 'submission-list',
+    data () {
       return {
-        submissions: []
+        fields: [
+          { key: 'type', sortable: true },
+          { key: 'score', label: 'Score', sortable: true },
+          { key: 'full_score', sortable: true }
+        ]
       }
     },
-    created: function () {
-      this.getSubmissions()
-    },
-    methods: {
-      getSubmissions () {
-        this.$http.get('http://localhost:8000/api/student/1').then(student => {
-          return Promise.all(student.body.submissions.map(id => {
-            return this.$http.get(`http://localhost:8000/api/submission/${id}`)
-          }))
-        }).then(response => {
-          console.log(response)
-          this.submissions = response.map(item => { return item.body })
-        }).catch(console.log.bind(console))
+    props: ['submissions'],
+    computed: {
+      sumScore () {
+        return this.submissions.map(a => a.score).reduce((a, b) => a + b)
+      },
+      sumFullScore () {
+        return this.submissions.map(a => a.full_score).reduce((a, b) => a + b)
+      },
+      pointRatio () {
+        return ((this.sumScore / this.sumFullScore) * 100).toFixed(2)
       }
     }
   }
diff --git a/frontend/src/main.js b/frontend/src/main.js
index 6dca5db2a7163800fa1b10a60694825bc3eb7a21..565189ceedf3e5314ab870b95b6d959192590d07 100644
--- a/frontend/src/main.js
+++ b/frontend/src/main.js
@@ -2,16 +2,20 @@
 // (runtime-only or standalone) has been set in webpack.base.conf with an alias.
 import Vue from 'vue'
 import App from './App'
+import BootstrapVue from 'bootstrap-vue'
 import router from './router'
-import VueResource from 'vue-resource'
+import store from './store/store'
+import 'bootstrap-vue/dist/bootstrap-vue.css'
+import 'bootstrap/dist/css/bootstrap.css'
 
-Vue.use(VueResource)
+Vue.use(BootstrapVue)
 
 Vue.config.productionTip = false
 
 /* eslint-disable no-new */
 new Vue({
   el: '#app',
+  store,
   router,
   template: '<App/>',
   components: { App }
diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js
index b00e372a7bc73b2775a2daa1caf317a10b337709..c29cbc7da105a411223133581fbde9ddec64aace 100644
--- a/frontend/src/router/index.js
+++ b/frontend/src/router/index.js
@@ -1,6 +1,7 @@
 import Vue from 'vue'
 import Router from 'vue-router'
-import SubmissionList from '@/components/student/SubmissionList'
+import Login from '@/components/Login'
+import StudentPage from '@/components/student/StudentPage'
 
 Vue.use(Router)
 
@@ -8,8 +9,13 @@ export default new Router({
   routes: [
     {
       path: '/',
-      name: 'Submissions',
-      component: SubmissionList
+      name: 'grady-login',
+      component: Login
+    },
+    {
+      path: '/student/',
+      name: 'student-page',
+      component: StudentPage
     }
   ]
 })
diff --git a/frontend/src/store/api.js b/frontend/src/store/api.js
index fd2c9786c5d2ecd567f4fcc253ece0423aed7153..368c09c86195ded2f5c97b1199a26e37c4184190 100644
--- a/frontend/src/store/api.js
+++ b/frontend/src/store/api.js
@@ -1,27 +1,7 @@
-import Vue from 'vue'
-import VueResource from 'vue-resource'
+import axios from 'axios'
 
-Vue.use(VueResource)
+var ax = axios.create({
+  baseURL: 'http://localhost:8000/'
+})
 
-export default {
-  get (url, request) {
-    return Vue.http.get(url, request)
-      .then((response) => Promise.resolve(response))
-      .catch((error) => Promise.reject(error))
-  },
-  post (url, request) {
-    return Vue.http.post(url, request)
-      .then((response) => Promise.resolve(response))
-      .catch((error) => Promise.reject(error))
-  },
-  patch (url, request) {
-    return Vue.http.patch(url, request)
-      .then((response) => Promise.resolve(response))
-      .catch((error) => Promise.reject(error))
-  },
-  delete (url, request) {
-    return Vue.http.delete(url, request)
-      .then((response) => Promise.resolve(response))
-      .catch((error) => Promise.reject(error))
-  }
-}
+export default ax
diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js
new file mode 100644
index 0000000000000000000000000000000000000000..150521a07241ce031fa1bd5454e3ba7a9e9f963f
--- /dev/null
+++ b/frontend/src/store/store.js
@@ -0,0 +1,39 @@
+import Vuex from 'vuex'
+import Vue from 'vue'
+import ax from './api'
+
+Vue.use(Vuex)
+
+const store = new Vuex.Store({
+  state: {
+    token: '',
+    loggedIn: false,
+    username: ''
+  },
+  mutations: {
+    'LOGIN': function (state, creds) {
+      state.token = creds.token
+      state.loggedIn = true
+      state.username = creds.username
+    },
+    'LOGOUT': function (state) {
+      state.token = ''
+      state.loggedIn = false
+    }
+  },
+  actions: {
+    async getToken (store, credentials) {
+      const response = await ax.post('api-token-auth/', credentials)
+      store.commit('LOGIN', {
+        token: response.data.token,
+        username: credentials.username
+      })
+      ax.defaults.headers.common['Authorization'] = 'JWT ' + response.data.token
+    },
+    logout (store) {
+      store.commit('LOGOUT')
+    }
+  }
+})
+
+export default store
diff --git a/frontend/test/unit/index.js b/frontend/test/unit/index.js
index c69f33fd8a0fb2796073222645c42c7c15188a6e..d0ddfd3846b6da1e934c141b7137655ccd6b13b2 100644
--- a/frontend/test/unit/index.js
+++ b/frontend/test/unit/index.js
@@ -1,4 +1,5 @@
 import Vue from 'vue'
+import 'es6-promise/auto'
 
 Vue.config.productionTip = false
 
diff --git a/frontend/test/unit/karma.conf.js b/frontend/test/unit/karma.conf.js
index 8e4951c9e4ecc597be347be1fd8e163cdbab13e2..b77a340f7b3796e08e7f60abee8bded5c26365df 100644
--- a/frontend/test/unit/karma.conf.js
+++ b/frontend/test/unit/karma.conf.js
@@ -7,10 +7,6 @@ var webpackConfig = require('../../build/webpack.test.conf')
 
 module.exports = function (config) {
   config.set({
-    // to run in additional browsers:
-    // 1. install corresponding karma launcher
-    //    http://karma-runner.github.io/0.13/config/browsers.html
-    // 2. add it to the `browsers` array below.
     browsers: ['PhantomJS'],
     frameworks: ['mocha', 'sinon-chai', 'phantomjs-shim'],
     reporters: ['spec', 'coverage'],
diff --git a/frontend/test/unit/specs/Hello.spec.js b/frontend/test/unit/specs/Hello.spec.js
deleted file mode 100644
index bfaa225c95f61e61e5e03bab02e25166f063199d..0000000000000000000000000000000000000000
--- a/frontend/test/unit/specs/Hello.spec.js
+++ /dev/null
@@ -1,11 +0,0 @@
-import Vue from 'vue'
-import HelloWorld from '@/components/HelloWorld'
-
-describe('HelloWorld.vue', () => {
-  it('should render correct contents', () => {
-    const Constructor = Vue.extend(HelloWorld)
-    const vm = new Constructor().$mount()
-    expect(vm.$el.querySelector('.hello h1').textContent)
-      .to.equal('Welcome to Your Vue.js App')
-  })
-})
diff --git a/frontend/test/unit/specs/SubmissionList.spec.js b/frontend/test/unit/specs/SubmissionList.spec.js
new file mode 100644
index 0000000000000000000000000000000000000000..8e855486029934dc781d0058d6bc7e11c7d2ac03
--- /dev/null
+++ b/frontend/test/unit/specs/SubmissionList.spec.js
@@ -0,0 +1,39 @@
+import Vue from 'vue'
+import SubmissionList from '@/components/student/SubmissionList'
+
+describe('SubmissionList.vue', () => {
+  it('tests the SubmissionList for students', () => {
+    const data = [{
+      'type': 'Aufgabe 01',
+      'text': 'I dont know the answer.',
+      'feedback': 'I am very disappointed.',
+      'score': 5,
+      'full_score': 14
+    },
+    {
+      'type': 'Aufgabe 01',
+      'text': 'A very good solution, indeed',
+      'feedback': 'I am still very disappointed.',
+      'score': 7,
+      'full_score': 10
+    }]
+
+    const Constructor = Vue.extend(SubmissionList)
+
+    const comp = new Constructor({
+      propsData: {
+        // Props are passed in "propsData".
+        submissions: data
+      }
+    }).$mount()
+
+    expect(comp.sumScore)
+      .to.equal(12)
+
+    expect(comp.sumFullScore)
+      .to.equal(24)
+
+    expect(comp.pointRatio)
+      .to.equal('50.00')
+  })
+})
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index e96d4143b7d515c3ddf002b6e8e53f7b28d526f9..d417ec11d84acfaefbada048d993b002dae8a9c2 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -220,13 +220,17 @@ async-each@^1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d"
 
+async-limiter@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8"
+
 async@1.x, async@^1.4.0, async@^1.5.2:
   version "1.5.2"
   resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a"
 
 async@^2.1.2, async@^2.4.1:
-  version "2.5.0"
-  resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d"
+  version "2.6.0"
+  resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4"
   dependencies:
     lodash "^4.14.0"
 
@@ -272,6 +276,13 @@ aws4@^1.2.1, aws4@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e"
 
+axios@^0.17.0:
+  version "0.17.0"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.17.0.tgz#7da747916db803f761651d6091d708789b953c6a"
+  dependencies:
+    follow-redirects "^1.2.3"
+    is-buffer "^1.1.5"
+
 babel-code-frame@^6.11.0, babel-code-frame@^6.16.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
   version "6.26.0"
   resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
@@ -741,6 +752,14 @@ babel-plugin-transform-strict-mode@^6.24.1:
     babel-runtime "^6.22.0"
     babel-types "^6.24.1"
 
+babel-polyfill@6.23.0:
+  version "6.23.0"
+  resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.23.0.tgz#8364ca62df8eafb830499f699177466c3b03499d"
+  dependencies:
+    babel-runtime "^6.22.0"
+    core-js "^2.4.0"
+    regenerator-runtime "^0.10.0"
+
 babel-preset-env@^1.3.2:
   version "1.6.1"
   resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48"
@@ -950,13 +969,16 @@ boom@5.x.x:
   dependencies:
     hoek "4.x.x"
 
-bootstrap-vue@^1.0.0-beta.9:
-  version "1.0.0-beta.9"
-  resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-1.0.0-beta.9.tgz#4e0bc5bcb95a90dc3bec7124ea3ddf5cc4c6ffa6"
+bootstrap-vue@^1.0.0:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/bootstrap-vue/-/bootstrap-vue-1.0.2.tgz#6fe0222be4e01572fa83ff4478549f260c0cff8b"
   dependencies:
-    bootstrap "^4.0.0-beta"
+    lodash.startcase "^4.4.0"
+    opencollective "^1.0.3"
+    popper.js "^1.12.6"
+    vue-functional-data-merge "^1.0.6"
 
-bootstrap@^4.0.0-beta:
+bootstrap@4.0.0-beta.2:
   version "4.0.0-beta.2"
   resolved "https://registry.yarnpkg.com/bootstrap/-/bootstrap-4.0.0-beta.2.tgz#4d67d2aa2219f062cd90bc1247e6747b9e8fd051"
 
@@ -1049,10 +1071,10 @@ browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6:
     electron-to-chromium "^1.2.7"
 
 browserslist@^2.1.2, browserslist@^2.5.1:
-  version "2.7.0"
-  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.7.0.tgz#dc375dc70048fec3d989042a35022342902eff00"
+  version "2.8.0"
+  resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.8.0.tgz#27d64028130a2e8585ca96f7c3b7730eff4de493"
   dependencies:
-    caniuse-lite "^1.0.30000757"
+    caniuse-lite "^1.0.30000758"
     electron-to-chromium "^1.3.27"
 
 buffer-xor@^1.0.3:
@@ -1129,12 +1151,12 @@ caniuse-api@^1.5.2:
     lodash.uniq "^4.5.0"
 
 caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639:
-  version "1.0.30000758"
-  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000758.tgz#a235627b1922e878b63164942c991b84de92c810"
+  version "1.0.30000760"
+  resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000760.tgz#3ea29473eb78a6ccb09f2eb73ac9e1debfec528d"
 
-caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000757:
-  version "1.0.30000758"
-  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000758.tgz#e261140076651049cf6891ed4bc649b5c8c26c69"
+caniuse-lite@^1.0.30000748, caniuse-lite@^1.0.30000758:
+  version "1.0.30000760"
+  resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000760.tgz#ec720395742f1c7ec8947fd6dd2604e77a8f98ff"
 
 caseless@~0.12.0:
   version "0.12.0"
@@ -1158,7 +1180,7 @@ chai@^4.1.2:
     pathval "^1.0.0"
     type-detect "^4.0.0"
 
-chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
+chalk@1.1.3, chalk@^1.0.0, chalk@^1.1.1, chalk@^1.1.3:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
   dependencies:
@@ -1255,8 +1277,8 @@ cliui@^3.2.0:
     wrap-ansi "^2.0.0"
 
 clone@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.2.tgz#260b7a99ebb1edfe247538175f783243cb19d149"
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f"
 
 co@^4.6.0:
   version "4.6.0"
@@ -1277,8 +1299,8 @@ code-point-at@^1.0.0:
   resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
 
 color-convert@^1.3.0, color-convert@^1.9.0:
-  version "1.9.0"
-  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.0.tgz#1accf97dd739b983bf994d56fec8f95853641b7a"
+  version "1.9.1"
+  resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
   dependencies:
     color-name "^1.1.1"
 
@@ -1366,6 +1388,13 @@ concat-stream@1.6.0, concat-stream@^1.5.2:
     readable-stream "^2.2.2"
     typedarray "^0.0.6"
 
+config-chain@~1.1.5:
+  version "1.1.11"
+  resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.11.tgz#aba09747dfbe4c3e70e766a6e41586e1859fc6f2"
+  dependencies:
+    ini "^1.3.4"
+    proto-list "~1.2.1"
+
 connect-history-api-fallback@^1.3.0:
   version "1.4.0"
   resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.4.0.tgz#3db24f973f4b923b0e82f619ce0df02411ca623d"
@@ -1673,7 +1702,7 @@ debug@2.6.8:
   dependencies:
     ms "2.0.0"
 
-debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.6.8:
+debug@2.6.9, debug@^2.1.1, debug@^2.2.0, debug@^2.6.8, debug@^2.6.9:
   version "2.6.9"
   resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
   dependencies:
@@ -1683,12 +1712,6 @@ decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
 
-decompress-response@^3.2.0:
-  version "3.3.0"
-  resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
-  dependencies:
-    mimic-response "^1.0.0"
-
 deep-eql@^3.0.0:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-3.0.1.tgz#dfc9404400ad1c8fe023e7da1df1c147c4b444df"
@@ -1852,10 +1875,6 @@ domutils@^1.5.1:
     dom-serializer "0"
     domelementtype "1"
 
-duplexer3@^0.1.4:
-  version "0.1.4"
-  resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
-
 duplexer@^0.1.1:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
@@ -1898,6 +1917,12 @@ encodeurl@~1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.1.tgz#79e3d58655346909fe6f0f45a5de68103b294d20"
 
+encoding@^0.1.11:
+  version "0.1.12"
+  resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
+  dependencies:
+    iconv-lite "~0.4.13"
+
 engine.io-client@1.8.3:
   version "1.8.3"
   resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-1.8.3.tgz#1798ed93451246453d4c6f635d7a201fe940d5ab"
@@ -1998,7 +2023,7 @@ es6-map@^0.1.3:
     es6-symbol "~3.1.1"
     event-emitter "~0.3.5"
 
-es6-promise@^4.0.3:
+es6-promise@^4.0.3, es6-promise@^4.1.1:
   version "4.1.1"
   resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.1.1.tgz#8811e90915d9a0dba36274f0b242dbda78f9c92a"
 
@@ -2325,6 +2350,14 @@ extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
 
+external-editor@^2.0.1:
+  version "2.0.5"
+  resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.0.5.tgz#52c249a3981b9ba187c7cacf5beb50bf1d91a6bc"
+  dependencies:
+    iconv-lite "^0.4.17"
+    jschardet "^1.4.2"
+    tmp "^0.0.33"
+
 extglob@^0.3.1:
   version "0.3.2"
   resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1"
@@ -2382,6 +2415,12 @@ figures@^1.3.5:
     escape-string-regexp "^1.0.5"
     object-assign "^4.1.0"
 
+figures@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
+  dependencies:
+    escape-string-regexp "^1.0.5"
+
 file-entry-cache@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361"
@@ -2480,6 +2519,12 @@ flatten@^1.0.2:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782"
 
+follow-redirects@^1.2.3:
+  version "1.2.5"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.2.5.tgz#ffd3e14cbdd5eaa72f61b6368c1f68516c2a26cc"
+  dependencies:
+    debug "^2.6.9"
+
 for-in@^1.0.1:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
@@ -2685,25 +2730,6 @@ globby@^5.0.0:
     pify "^2.0.0"
     pinkie-promise "^2.0.0"
 
-got@^7.0.0:
-  version "7.1.0"
-  resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
-  dependencies:
-    decompress-response "^3.2.0"
-    duplexer3 "^0.1.4"
-    get-stream "^3.0.0"
-    is-plain-obj "^1.1.0"
-    is-retry-allowed "^1.0.0"
-    is-stream "^1.0.0"
-    isurl "^1.0.0-alpha5"
-    lowercase-keys "^1.0.0"
-    p-cancelable "^0.3.0"
-    p-timeout "^1.1.1"
-    safe-buffer "^5.0.1"
-    timed-out "^4.0.0"
-    url-parse-lax "^1.0.0"
-    url-to-options "^1.0.1"
-
 graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9:
   version "4.1.11"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658"
@@ -2778,16 +2804,6 @@ has-flag@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
 
-has-symbol-support-x@^1.4.1:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.1.tgz#66ec2e377e0c7d7ccedb07a3a84d77510ff1bc4c"
-
-has-to-string-tag-x@^1.2.0:
-  version "1.4.1"
-  resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
-  dependencies:
-    has-symbol-support-x "^1.4.1"
-
 has-unicode@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
@@ -2975,7 +2991,7 @@ https-browserify@0.0.1:
   version "0.0.1"
   resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-0.0.1.tgz#3f91365cabe60b77ed0ebba24b454e3e09d95a82"
 
-iconv-lite@0.4.19:
+iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13:
   version "0.4.19"
   resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b"
 
@@ -3030,7 +3046,7 @@ inherits@2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1"
 
-ini@~1.3.0:
+ini@^1.3.4, ini@~1.3.0:
   version "1.3.4"
   resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e"
 
@@ -3040,6 +3056,24 @@ inject-loader@^3.0.0:
   dependencies:
     babel-core "~6"
 
+inquirer@3.0.6:
+  version "3.0.6"
+  resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.0.6.tgz#e04aaa9d05b7a3cb9b0f407d04375f0447190347"
+  dependencies:
+    ansi-escapes "^1.1.0"
+    chalk "^1.0.0"
+    cli-cursor "^2.1.0"
+    cli-width "^2.0.0"
+    external-editor "^2.0.1"
+    figures "^2.0.0"
+    lodash "^4.3.0"
+    mute-stream "0.0.7"
+    run-async "^2.2.0"
+    rx "^4.1.0"
+    string-width "^2.0.0"
+    strip-ansi "^3.0.0"
+    through "^2.3.6"
+
 inquirer@^0.12.0:
   version "0.12.0"
   resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-0.12.0.tgz#1ef2bfd63504df0bc75785fff8c2c41df12f077e"
@@ -3185,10 +3219,6 @@ is-number@^3.0.0:
   dependencies:
     kind-of "^3.0.2"
 
-is-object@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470"
-
 is-path-cwd@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d"
@@ -3205,7 +3235,7 @@ is-path-inside@^1.0.0:
   dependencies:
     path-is-inside "^1.0.1"
 
-is-plain-obj@^1.0.0, is-plain-obj@^1.1.0:
+is-plain-obj@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e"
 
@@ -3217,6 +3247,10 @@ is-primitive@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
 
+is-promise@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
+
 is-property@^1.0.0:
   version "1.0.2"
   resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84"
@@ -3227,11 +3261,7 @@ is-resolvable@^1.0.0:
   dependencies:
     tryit "^1.0.1"
 
-is-retry-allowed@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34"
-
-is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0:
+is-stream@^1.0.1, is-stream@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
 
@@ -3318,13 +3348,6 @@ istanbul@^0.4.0:
     which "^1.1.1"
     wordwrap "^1.0.0"
 
-isurl@^1.0.0-alpha5:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
-  dependencies:
-    has-to-string-tag-x "^1.2.0"
-    is-object "^1.0.1"
-
 js-base64@^2.1.9:
   version "2.3.2"
   resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.3.2.tgz#a79a923666372b580f8e27f51845c6f7e8fbfbaf"
@@ -3351,6 +3374,10 @@ jsbn@~0.1.0:
   version "0.1.1"
   resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
 
+jschardet@^1.4.2:
+  version "1.6.0"
+  resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-1.6.0.tgz#c7d1a71edcff2839db2f9ec30fc5d5ebd3c1a678"
+
 jsesc@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b"
@@ -3672,6 +3699,10 @@ lodash.memoize@^4.1.2:
   version "4.1.2"
   resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
 
+lodash.startcase@^4.4.0:
+  version "4.4.0"
+  resolved "https://registry.yarnpkg.com/lodash.startcase/-/lodash.startcase-4.4.0.tgz#9436e34ed26093ed7ffae1936144350915d9add8"
+
 lodash.uniq@^4.5.0:
   version "4.5.0"
   resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
@@ -3701,9 +3732,9 @@ lolex@^1.6.0:
   version "1.6.0"
   resolved "https://registry.yarnpkg.com/lolex/-/lolex-1.6.0.tgz#3a9a0283452a47d7439e72731b9e07d7386e49f6"
 
-lolex@^2.1.3:
-  version "2.1.3"
-  resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.1.3.tgz#53f893bbe88c80378156240e127126b905c83087"
+lolex@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/lolex/-/lolex-2.3.0.tgz#d6bad0f0aa5caebffcfebb09fb2caa89baaff51c"
 
 longest@^1.0.1:
   version "1.0.1"
@@ -3726,10 +3757,6 @@ lower-case@^1.1.1:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
 
-lowercase-keys@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
-
 lru-cache@2.2.x:
   version "2.2.4"
   resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.2.4.tgz#6c658619becf14031d0d0b594b16042ce4dc063d"
@@ -3853,10 +3880,6 @@ mimic-fn@^1.0.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.1.0.tgz#e667783d92e89dbd342818b5230b9d62a672ad18"
 
-mimic-response@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e"
-
 minimalistic-assert@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3"
@@ -3928,6 +3951,10 @@ mute-stream@0.0.5:
   version "0.0.5"
   resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0"
 
+mute-stream@0.0.7:
+  version "0.0.7"
+  resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab"
+
 nan@^2.3.0:
   version "2.7.0"
   resolved "https://registry.yarnpkg.com/nan/-/nan-2.7.0.tgz#d95bf721ec877e08db276ed3fc6eb78f9083ad46"
@@ -3968,6 +3995,13 @@ node-dir@^0.1.10:
   dependencies:
     minimatch "^3.0.2"
 
+node-fetch@1.6.3:
+  version "1.6.3"
+  resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.6.3.tgz#dc234edd6489982d58e8f0db4f695029abcd8c04"
+  dependencies:
+    encoding "^0.1.11"
+    is-stream "^1.0.1"
+
 node-libs-browser@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.0.0.tgz#a3a59ec97024985b46e958379646f96c4b616646"
@@ -4012,7 +4046,7 @@ node-pre-gyp@^0.6.36:
     tar "^2.2.1"
     tar-pack "^3.4.0"
 
-nopt@3.x:
+nopt@3.x, nopt@~3.0.1:
   version "3.0.6"
   resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
   dependencies:
@@ -4131,10 +4165,28 @@ onetime@^2.0.0:
   dependencies:
     mimic-fn "^1.0.0"
 
+opencollective@^1.0.3:
+  version "1.0.3"
+  resolved "https://registry.yarnpkg.com/opencollective/-/opencollective-1.0.3.tgz#aee6372bc28144583690c3ca8daecfc120dd0ef1"
+  dependencies:
+    babel-polyfill "6.23.0"
+    chalk "1.1.3"
+    inquirer "3.0.6"
+    minimist "1.2.0"
+    node-fetch "1.6.3"
+    opn "4.0.2"
+
 opener@^1.4.3:
   version "1.4.3"
   resolved "https://registry.yarnpkg.com/opener/-/opener-1.4.3.tgz#5c6da2c5d7e5831e8ffa3964950f8d6674ac90b8"
 
+opn@4.0.2:
+  version "4.0.2"
+  resolved "https://registry.yarnpkg.com/opn/-/opn-4.0.2.tgz#7abc22e644dff63b0a96d5ab7f2790c0f01abc95"
+  dependencies:
+    object-assign "^4.0.1"
+    pinkie-promise "^2.0.0"
+
 opn@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/opn/-/opn-5.1.0.tgz#72ce2306a17dbea58ff1041853352b4a8fc77519"
@@ -4206,10 +4258,6 @@ osenv@^0.1.4:
     os-homedir "^1.0.0"
     os-tmpdir "^1.0.0"
 
-p-cancelable@^0.3.0:
-  version "0.3.0"
-  resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa"
-
 p-finally@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
@@ -4224,12 +4272,6 @@ p-locate@^2.0.0:
   dependencies:
     p-limit "^1.1.0"
 
-p-timeout@^1.1.1:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.0.tgz#9820f99434c5817868b4f34809ee5291660d5b6c"
-  dependencies:
-    p-finally "^1.0.0"
-
 pako@~0.2.0:
   version "0.2.9"
   resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75"
@@ -4415,6 +4457,10 @@ pluralize@^1.2.1:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45"
 
+popper.js@^1.12.6:
+  version "1.12.6"
+  resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.12.6.tgz#91e12a97b07815258b76915d64044e8ac053d426"
+
 portfinder@^1.0.13:
   version "1.0.13"
   resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9"
@@ -4569,8 +4615,8 @@ postcss-minify-selectors@^2.0.4:
     postcss-selector-parser "^2.0.0"
 
 postcss-modules-extract-imports@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.1.0.tgz#b614c9720be6816eaee35fb3a5faa1dba6a05ddb"
+  version "1.2.0"
+  resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85"
   dependencies:
     postcss "^6.0.1"
 
@@ -4696,7 +4742,7 @@ prelude-ls@~1.1.2:
   version "1.1.2"
   resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
 
-prepend-http@^1.0.0, prepend-http@^1.0.1:
+prepend-http@^1.0.0:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
 
@@ -4705,8 +4751,8 @@ preserve@^0.2.0:
   resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b"
 
 prettier@^1.7.0:
-  version "1.7.4"
-  resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.7.4.tgz#5e8624ae9363c80f95ec644584ecdf55d74f93fa"
+  version "1.8.1"
+  resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.8.1.tgz#91064d778c08c85ac1cbe6b23195c34310d039f9"
 
 pretty-error@^2.0.2:
   version "2.1.1"
@@ -4731,6 +4777,10 @@ progress@^1.1.8:
   version "1.1.8"
   resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
 
+proto-list@~1.2.1:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
+
 proxy-addr@~2.0.2:
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.2.tgz#6571504f47bb988ec8180253f85dd7e14952bdec"
@@ -4936,6 +4986,10 @@ regenerate@^1.2.1:
   version "1.3.3"
   resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f"
 
+regenerator-runtime@^0.10.0:
+  version "0.10.5"
+  resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658"
+
 regenerator-runtime@^0.11.0:
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz#7e54fe5b5ccd5d6624ea6255c3473be090b802e1"
@@ -5152,18 +5206,24 @@ run-async@^0.1.0:
   dependencies:
     once "^1.3.0"
 
+run-async@^2.2.0:
+  version "2.3.0"
+  resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0"
+  dependencies:
+    is-promise "^2.1.0"
+
 rx-lite@^3.1.2:
   version "3.1.2"
   resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
 
+rx@^4.1.0:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/rx/-/rx-4.1.0.tgz#a5f13ff79ef3b740fe30aa803fb09f98805d4782"
+
 safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
   version "5.1.1"
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853"
 
-safe-buffer@~5.0.1:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7"
-
 samsam@1.x:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50"
@@ -5271,13 +5331,13 @@ sinon-chai@^2.8.0:
   resolved "https://registry.yarnpkg.com/sinon-chai/-/sinon-chai-2.14.0.tgz#da7dd4cc83cd6a260b67cca0f7a9fdae26a1205d"
 
 sinon@^4.0.0:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.1.tgz#bd657be815df608887fe1f0b75f9590ec563748a"
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-4.1.2.tgz#65610521d926fb53742dd84cd599f0b89a82f440"
   dependencies:
     diff "^3.1.0"
     formatio "1.2.0"
     lodash.get "^4.4.2"
-    lolex "^2.1.3"
+    lolex "^2.2.0"
     nise "^1.2.0"
     supports-color "^4.4.0"
     type-detect "^4.0.0"
@@ -5362,7 +5422,7 @@ source-map-support@^0.4.15:
   dependencies:
     source-map "^0.5.6"
 
-source-map@0.5.x, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1, source-map@~0.5.3:
+source-map@0.5.x, source-map@^0.5.1, source-map@^0.5.3, source-map@^0.5.6, source-map@~0.5.1:
   version "0.5.7"
   resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc"
 
@@ -5624,10 +5684,6 @@ time-stamp@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357"
 
-timed-out@^4.0.0:
-  version "4.0.1"
-  resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
-
 timers-browserify@^2.0.2:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.4.tgz#96ca53f4b794a5e7c0e1bd7cc88a372298fa01e6"
@@ -5640,7 +5696,7 @@ tmp@0.0.31:
   dependencies:
     os-tmpdir "~1.0.1"
 
-tmp@0.0.x:
+tmp@0.0.x, tmp@^0.0.33:
   version "0.0.33"
   resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
   dependencies:
@@ -5716,8 +5772,8 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
 
 uglify-js@3.1.x:
-  version "3.1.6"
-  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.6.tgz#918832602036e95d2318e11f27ee8461a8592c5d"
+  version "3.1.8"
+  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.1.8.tgz#780d08b4f6782fe36ea5484d952362eddaf1d7b8"
   dependencies:
     commander "~2.11.0"
     source-map "~0.6.1"
@@ -5788,16 +5844,6 @@ url-loader@^0.5.8:
     loader-utils "^1.0.2"
     mime "1.3.x"
 
-url-parse-lax@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73"
-  dependencies:
-    prepend-http "^1.0.1"
-
-url-to-options@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
-
 url@^0.11.0:
   version "0.11.0"
   resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
@@ -5877,13 +5923,25 @@ void-elements@^2.0.0:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec"
 
+vue-beautify@^1.1.3:
+  version "1.1.3"
+  resolved "https://registry.yarnpkg.com/vue-beautify/-/vue-beautify-1.1.3.tgz#69d193b641db0047e26378858bb759754ee53692"
+  dependencies:
+    config-chain "~1.1.5"
+    mkdirp "~0.5.0"
+    nopt "~3.0.1"
+
+vue-functional-data-merge@^1.0.6:
+  version "1.0.6"
+  resolved "https://registry.yarnpkg.com/vue-functional-data-merge/-/vue-functional-data-merge-1.0.6.tgz#cde4f0cf3f251f7f0196341156d2c936cca63d40"
+
 vue-hot-reload-api@^2.2.0:
-  version "2.2.0"
-  resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.0.tgz#9a21b35ced3634434a43ee80efb7350ea8fb206d"
+  version "2.2.3"
+  resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.2.3.tgz#43c8e5506d65a271d2571936d77253019fd3eb17"
 
 vue-loader@^13.3.0:
-  version "13.4.0"
-  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.4.0.tgz#44aa7eef4fb1be89fbc37193ad33b8253417c4b9"
+  version "13.5.0"
+  resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-13.5.0.tgz#52f7b3790a267eff80012b77ea187a54586dd5d4"
   dependencies:
     consolidate "^0.14.0"
     hash-sum "^1.0.2"
@@ -5899,12 +5957,6 @@ vue-loader@^13.3.0:
     vue-style-loader "^3.0.0"
     vue-template-es2015-compiler "^1.6.0"
 
-vue-resource@^1.3.4:
-  version "1.3.4"
-  resolved "https://registry.yarnpkg.com/vue-resource/-/vue-resource-1.3.4.tgz#9fc0bdf6a2f5cab430129fc99d347b3deae7b099"
-  dependencies:
-    got "^7.0.0"
-
 vue-router@^3.0.1:
   version "3.0.1"
   resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.0.1.tgz#d9b05ad9c7420ba0f626d6500d693e60092cc1e9"
@@ -5917,8 +5969,8 @@ vue-style-loader@^3.0.0, vue-style-loader@^3.0.1:
     loader-utils "^1.0.2"
 
 vue-template-compiler@^2.5.2:
-  version "2.5.2"
-  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.2.tgz#6f198ebc677b8f804315cd33b91e849315ae7177"
+  version "2.5.3"
+  resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.5.3.tgz#ab631b0694e211a6aaf0d800102b37836aae36a4"
   dependencies:
     de-indent "^1.0.2"
     he "^1.1.0"
@@ -5928,8 +5980,8 @@ vue-template-es2015-compiler@^1.6.0:
   resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.6.0.tgz#dc42697133302ce3017524356a6c61b7b69b4a18"
 
 vue@^2.5.2:
-  version "2.5.2"
-  resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.2.tgz#fd367a87bae7535e47f9dc5c9ec3b496e5feb5a4"
+  version "2.5.3"
+  resolved "https://registry.yarnpkg.com/vue/-/vue-2.5.3.tgz#e1a3b1f49b6e93e574ce040b95cbc873912fecc1"
 
 vuex@^3.0.1:
   version "3.0.1"
@@ -5944,8 +5996,8 @@ watchpack@^1.4.0:
     graceful-fs "^4.1.2"
 
 webpack-bundle-analyzer@^2.9.0:
-  version "2.9.0"
-  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.9.0.tgz#b58bc34cc30b27ffdbaf3d00bf27aba6fa29c6e3"
+  version "2.9.1"
+  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-2.9.1.tgz#c2c8e03e8e5768ed288b39ae9e27a8b8d7b9d476"
   dependencies:
     acorn "^5.1.1"
     chalk "^1.1.3"
@@ -5957,7 +6009,7 @@ webpack-bundle-analyzer@^2.9.0:
     lodash "^4.17.4"
     mkdirp "^0.5.1"
     opener "^1.4.3"
-    ws "^2.3.1"
+    ws "^3.3.1"
 
 webpack-dev-middleware@^1.0.11, webpack-dev-middleware@^1.12.0:
   version "1.12.0"
@@ -5985,11 +6037,11 @@ webpack-merge@^4.1.0:
     lodash "^4.17.4"
 
 webpack-sources@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.1.tgz#c7356436a4d13123be2e2426a05d1dad9cbe65cf"
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.0.2.tgz#d0148ec083b3b5ccef1035a6b3ec16442983b27a"
   dependencies:
     source-list-map "^2.0.0"
-    source-map "~0.5.3"
+    source-map "~0.6.1"
 
 webpack@^3.6.0:
   version "3.8.1"
@@ -6078,11 +6130,12 @@ ws@1.1.2:
     options ">=0.0.5"
     ultron "1.0.x"
 
-ws@^2.3.1:
-  version "2.3.1"
-  resolved "https://registry.yarnpkg.com/ws/-/ws-2.3.1.tgz#6b94b3e447cb6a363f785eaf94af6359e8e81c80"
+ws@^3.3.1:
+  version "3.3.1"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.1.tgz#d97e34dee06a1190c61ac1e95f43cb60b78cf939"
   dependencies:
-    safe-buffer "~5.0.1"
+    async-limiter "~1.0.0"
+    safe-buffer "~5.1.0"
     ultron "~1.1.0"
 
 wtf-8@1.0.0: