diff --git a/core/migrations/0008_auto_20180219_1712.py b/core/migrations/0008_auto_20180219_1712.py
new file mode 100644
index 0000000000000000000000000000000000000000..afe81921d62cfac26894bf652477de58ba92bc7d
--- /dev/null
+++ b/core/migrations/0008_auto_20180219_1712.py
@@ -0,0 +1,23 @@
+# Generated by Django 2.0.2 on 2018-02-19 17:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('core', '0007_auto_20180217_2049'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='submissiontype',
+            name='programming_language',
+            field=models.CharField(choices=[('c', 'C syntax highlighting'), ('java', 'Java syntax highlighting')], default='c', max_length=25),
+        ),
+        migrations.AlterField(
+            model_name='submissiontype',
+            name='name',
+            field=models.CharField(max_length=100, unique=True),
+        ),
+    ]
diff --git a/core/models.py b/core/models.py
index 63b289a814fb2490c29e9922877025de862658c6..b903dfda6fe253c449d2b12458b31919d52395a9 100644
--- a/core/models.py
+++ b/core/models.py
@@ -105,13 +105,25 @@ class SubmissionType(models.Model):
     solution : TextField
         A sample solution or a correction guideline
     """
+
+    C = 'c'
+    JAVA = 'java'
+
+    LANGUAGE_CHOICES = (
+        (C, 'C syntax highlighting'),
+        (JAVA, 'Java syntax highlighting'),
+    )
+
     submission_type_id = models.UUIDField(primary_key=True,
                                           default=uuid.uuid4,
                                           editable=False)
-    name = models.CharField(max_length=50, unique=True)
+    name = models.CharField(max_length=100, unique=True)
     full_score = models.PositiveIntegerField(default=0)
     description = models.TextField()
     solution = models.TextField()
+    programming_language = models.CharField(max_length=25,
+                                            choices=LANGUAGE_CHOICES,
+                                            default=C)
 
     def __str__(self) -> str:
         return self.name
@@ -243,7 +255,7 @@ class StudentInfo(models.Model):
         if self.submissions.all():
             return OrderedDict({
                 s.type: s.feedback.score if hasattr(s, 'feedback') else 0
-                for s in self.submissions.all()
+                for s in self.submissions.order_by('type__name')
             })
 
         return OrderedDict({
diff --git a/core/serializers/common_serializers.py b/core/serializers/common_serializers.py
index 5729947a4da711ed6964f78268144b5fab4a2170..b1de1b0e95880f7c4af38132ce95caf2c0e6edbd 100644
--- a/core/serializers/common_serializers.py
+++ b/core/serializers/common_serializers.py
@@ -37,7 +37,12 @@ class SubmissionTypeSerializer(SubmissionTypeListSerializer):
 
     class Meta:
         model = models.SubmissionType
-        fields = ('pk', 'name', 'full_score', 'description', 'solution')
+        fields = ('pk',
+                  'name',
+                  'full_score',
+                  'description',
+                  'solution',
+                  'programming_language')
 
 
 class TutorSerializer(DynamicFieldsModelSerializer):
diff --git a/core/tests/test_export.py b/core/tests/test_export.py
index 9165bb4768a86cd5f117561d8253b4ed45adcb93..5cdaf1f1b1a8a08de5bf36eb12261f39486ee064 100644
--- a/core/tests/test_export.py
+++ b/core/tests/test_export.py
@@ -74,7 +74,7 @@ class ExportCSVTest(TestCase):
         client = Client()
         client.force_login(user=self.data['reviewers'][0])
 
-        self.response = client.get('/export/csv/')
+        self.response = client.get('/api/export/csv/')
 
     def test_can_access(self):
         self.assertEqual(status.HTTP_200_OK, self.response.status_code)
diff --git a/core/urls.py b/core/urls.py
index d47a108598e09bac55de7ff7dd3dd9e1451cbd9f..715379d0180a2e4d97bb6d89cddb268d87d80817 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -30,7 +30,8 @@ regular_views_urlpatterns = [
     path('user-role/', views.get_user_role, name='user-role'),
     path('jwt-time-delta/',
          views.get_jwt_expiration_delta,
-         name='jwt-time-delta')
+         name='jwt-time-delta'),
+    path('export/csv/', views.StudentCSVExport.as_view(), name='export-csv'),
 ]
 
 urlpatterns = [
diff --git a/core/views/export.py b/core/views/export.py
index e08fa7d854b56b288afc8676e166fc13e1860277..2375a25364ce6561a835659a1e5013be87da7ec5 100644
--- a/core/views/export.py
+++ b/core/views/export.py
@@ -1,39 +1,34 @@
-import csv
-
-from django.contrib.auth.mixins import PermissionRequiredMixin
-from django.http import HttpResponse
-from django.views.generic import View
-
 from core.models import StudentInfo, SubmissionType
 from core.permissions import IsReviewer
 
+from rest_framework.views import APIView
+from rest_framework_csv import renderers
+from rest_framework.response import Response
 
-class StudentCSVExport(PermissionRequiredMixin, View):
 
-    def has_permission(self):
-        return IsReviewer().has_permission(self.request, self)
+class StudentCSVExport(APIView):
+    renderer_classes = (renderers.CSVRenderer, )
+    permission_classes = (IsReviewer, )
 
-    def get(self, request, format=None):
-        # Create the HttpResponse object with the appropriate CSV header.
-        response = HttpResponse(content_type='text/csv')
-        response['Content-Disposition'] = 'attachment; filename="results.csv"'
+    def get_renderer_context(self):
+        context = super().get_renderer_context()
+        context['header'] = ('Matrikel', 'Username', 'Name', 'Sum',
+                             *SubmissionType.objects.values_list('name',
+                                                                 flat=True))
+        return context
 
-        writer = csv.writer(response)
-        writer.writerow(['Matrikel',
-                         'Username',
-                         'Name',
-                         'Sum',
-                         *SubmissionType.objects
-                         .order_by('name')
-                         .values_list('name', flat=True)])
+    def finalize_response(self, request, response, *args, **kwargs):
+        response['Content-Disposition'] = \
+            "attachment; filename=%s" % 'results.csv'
+        return super().finalize_response(request, response, *args, **kwargs)
 
-        for student in StudentInfo.get_annotated_score_submission_list():
-            writer.writerow([
-                student.matrikel_no,
-                student.user.username,
-                student.user.fullname,
-                student.overall_score,
-                *student.score_per_submission().values()
-            ])
-
-        return response
+    def get(self, request, format=None):
+        content = [{'Matrikel': student.matrikel_no,
+                    'Username': student.user.username,
+                    'Name': student.user.fullname,
+                    'Sum': student.overall_score,
+                    **student.score_per_submission()
+                    } for student
+                   in StudentInfo.get_annotated_score_submission_list()]
+
+        return Response(content)
diff --git a/grady/urls.py b/grady/urls.py
index ae77b57f317046247e23621c766eb26f09bd5c76..ea668ce094a5a5ed433d20af3d5073a9f333cc98 100644
--- a/grady/urls.py
+++ b/grady/urls.py
@@ -3,8 +3,6 @@ from django.urls import include, path
 from django.views.generic.base import TemplateView
 from rest_framework_jwt.views import obtain_jwt_token, refresh_jwt_token
 
-from core import views
-
 urlpatterns = [
     path('admin/', admin.site.urls),
     path('api/', include('core.urls')),
@@ -13,5 +11,4 @@ urlpatterns = [
     path('api-auth/', include('rest_framework.urls',
                               namespace='rest_framework')),
     path('', TemplateView.as_view(template_name='index.html')),
-    path('export/csv/', views.StudentCSVExport.as_view(), name='export-csv'),
 ]
diff --git a/requirements.txt b/requirements.txt
index 9f22e122200338b0f281b62485df5bdaa91b3a90..720f3bcb54ecc486b1061c8d25ba755697bd769d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,5 +1,6 @@
 django-cors-headers~=2.1.0
 django-extensions~=1.7.7
+djangorestframework-csv~=2.0.0
 djangorestframework-jwt~=1.11.0
 djangorestframework~=3.7.7
 Django~=2.0
@@ -7,6 +8,6 @@ drf-dynamic-fields~=0.2.0
 gevent~=1.2.2
 gunicorn~=19.7.0
 psycopg2-binary~=2.7.4
+tqdm~=4.19.5
 whitenoise~=3.3.1
 xlrd~=1.0.0
-tqdm~=4.19.5
diff --git a/util/importer.py b/util/importer.py
index 7172381179260a380e4636601d08881d47b25710..80e6fed1414b2bee8f3dfd07c4e13ae3ca01b10e 100644
--- a/util/importer.py
+++ b/util/importer.py
@@ -136,7 +136,6 @@ def add_submission(student_obj, code, tests, type):
         add_tests(submission_obj, tests)
 
 
-@transaction.atomic
 def call_loader(func: Callable) -> None:
     """ This function handles if a function will be executed at all. Currently
     it just checks in the RECORDS file for the name of the function. If it is
@@ -154,7 +153,8 @@ def call_loader(func: Callable) -> None:
             if not i('Proceed anyway?', NO):
                 return
 
-    func()  # This executes the specified loader
+    with transaction.atomic():
+        func()  # This executes the specified loader
 
     with open(RECORDS, 'a') as records_f:
         records_f.write(func.__name__)
@@ -181,7 +181,8 @@ def do_load_submission_types():
     print(
         '''For the following import you need three files:
 
-    1) A .csv file where the columns are: id, name, score
+    1) A .csv file where the columns are: id, name, score, (suffix). No
+       suffix defaults to .c
     2) A path to a directory where I can find sample solutions named
         <id>-lsg.c
     3) A path to a directory where I can find HTML files with an accurate
@@ -189,8 +190,8 @@ def do_load_submission_types():
 
     Example:
         $ cat submission_types.csv
-        a01, Alpha Team, 10
-        a02, Beta Distribution, 10
+        a01, Alpha Team, 10, .c
+        a02, Beta Distribution, 10, .java
         a03, Gamma Ray, 20
 
         $ tree -L 2
@@ -216,9 +217,17 @@ def do_load_submission_types():
             csv_rows = [row for row in csv.reader(tfile)]
 
         for row in csv_rows:
-            tid, name, score = (col.strip() for col in row)
+            tid, name, score, *lang = (col.strip() for col in row)
+
+            if not lang:
+                lang = '.c'
+            else:
+                lang = lang[0]
+
+            lang = lang.lower().strip('.')
+
             with \
-                open(os.path.join(lsg_dir, tid + '.c'),
+                open(os.path.join(lsg_dir, tid + '.' + lang),
                      encoding='utf-8') as lsg, \
                 open(os.path.join(desc_dir, tid + '.html'),
                      encoding='utf-8') as desc:
@@ -227,6 +236,7 @@ def do_load_submission_types():
                     'description': desc.read(),
                     'solution': lsg.read(),
                     'full_score': int(score),
+                    'programming_language': lang
                 }
             _, created = SubmissionType.objects.update_or_create(
                 name=name,