diff --git a/.auta.yml b/.auta.yml
index 6a9c5f8a6f70a1a143541deaa3a8a17e70d2ddf8..e57980c18d209b195ec487a9b21083653dbc04a3 100644
--- a/.auta.yml
+++ b/.auta.yml
@@ -1,3 +1,9 @@
 suppressions:
   - path: 'worker/src/test/resources/**'
     justification: Worker test resources are intentionally bad to test analyzers
+
+upload_suppressions:
+  - pattern: '.git'
+    justification: 'the git folder is part of version control and should be excluded'
+  - pattern: '.gitlab'
+    justification: 'this folder contains templates for issues, and is not relevant for code quality'
\ No newline at end of file
diff --git a/core/build.gradle b/core/build.gradle
index dcf1b1158d5f65e12806e9faf2e12af2623dee8b..1775f5d3085c443d4f5e09b6dd81fdf149812a1d 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -32,7 +32,7 @@ dependencies {
     implementation 'commons-fileupload:commons-fileupload:1.4'
 	implementation 'org.apache.httpcomponents:httpclient:4.5.+'
     implementation 'org.apache.httpcomponents:httpmime:4.5.+'
-    implementation 'javax.validation:validation-api:2.0.1.Final'
+    implementation 'org.hibernate.validator:hibernate-validator:6.0.17.Final'
     implementation 'org.apache.commons:commons-csv:1.6'
     implementation project(':')
 
diff --git a/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CPMController.java b/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CPMController.java
index 1cf72dbc3d2700a84a8af01840bd11bd0cfa1f48..31ab6ac541e3fdc1f59ee8116c9fa9108b49031a 100644
--- a/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CPMController.java
+++ b/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CPMController.java
@@ -6,6 +6,7 @@ import nl.tudelft.ewi.auta.core.database.IdentityContainer;
 import nl.tudelft.ewi.auta.core.database.Repositories;
 import nl.tudelft.ewi.auta.core.jobs.JobQueue;
 import nl.tudelft.ewi.auta.core.model.Assignment;
+import nl.tudelft.ewi.auta.core.model.CPMDataModel;
 import nl.tudelft.ewi.auta.core.model.FileStore;
 import nl.tudelft.ewi.auta.core.model.Job;
 import nl.tudelft.ewi.auta.core.model.Submission;
@@ -23,17 +24,19 @@ import org.slf4j.LoggerFactory;
 import org.springframework.context.ApplicationListener;
 import org.springframework.context.annotation.Bean;
 import org.springframework.http.ResponseEntity;
+import org.springframework.validation.BindingResult;
+import org.springframework.web.bind.annotation.ModelAttribute;
 import org.springframework.web.bind.annotation.PathVariable;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.RequestPart;
 import org.springframework.web.bind.annotation.RestController;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartResolver;
 import org.springframework.web.multipart.support.StandardServletMultipartResolver;
 
 import javax.annotation.Nullable;
+import javax.validation.Valid;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.MalformedURLException;
@@ -114,17 +117,14 @@ public class CPMController extends ControllerBase
     @RequestMapping(path = "/api/v1/cpm/{aid}", method = RequestMethod.POST)
     public ResponseEntity<Response> uploadAction(
             final @RequestParam(value = "file") MultipartFile file,
-            final @RequestParam(value = "projectName") String projectName,
-            final @RequestParam(value = "workunitID") String workunitID,
-            final @RequestPart(value = "workunitName") String workunitName,
-            final @RequestPart(value = "memberName") String memberName,
-            final @RequestPart(value = "uploadComments", required = false) String uploadComments,
-            final @RequestParam(value = "verificationCode") String verificationCode,
-            final @RequestPart(value = "filename") String filename,
-            final @PathVariable String aid
-            ) throws IOException {
+            final @PathVariable String aid,
+            final @Valid @ModelAttribute CPMDataModel model,
+            final BindingResult result) throws IOException {
 
+        this.checkForErrors(result);
         var res = new Response();
+        final var filename = model.getFilename();
+
         final var assignmentRepository = this.repositories.getAssignmentRepository();
         final var submissionRepository = this.repositories.getSubmissionRepository();
 
@@ -134,26 +134,30 @@ public class CPMController extends ControllerBase
         this.files.add(filename, temp);
 
         final var assignmentOptional = assignmentRepository.findById(aid);
-        if (!assignmentOptional.isPresent()) {
+        if (assignmentOptional.isEmpty()) {
             res.addError(ErrorCode.NO_SUCH_ASSIGNMENT, "There is no assignment with ID " + aid);
             return this.notFound().body(res);
         }
         var assignment = assignmentOptional.get();
-        assignment = this.addCPMdetails(projectName, workunitID, workunitName, assignment);
+        this.addCPMDetails(model, assignment);
         assignment = assignmentRepository.save(assignment);
 
-        var submission = new Submission(projectName + ":" + workunitName,
+        var submission = new Submission(model.getSubmissionName(),
                 filename, aid);
-        submission.setCpmVerificationCode(verificationCode);
+        submission.setCpmVerificationCode(model.getVerificationCode());
         submission.setAssignmentId(aid);
         submission.getPipelineLog().setSubmitted(Instant.now());
         submission.setContents(filename);
         submission = submissionRepository.save(submission);
 
+        // Once saved in the repository, the id should never be null.
+        assert submission.getId() != null;
+        final var sid = submission.getId();
 
-        var sid = submission.getId();
         logger.debug("Created new submission for CPM submission, submission id {}", sid);
 
+        final var memberName = model.getMemberName();
+
         this.repositories.getIdentityRepository().save(new IdentityContainer(sid, memberName));
         logger.debug("CPM submission {} belongs to {}", sid, memberName);
 
@@ -218,27 +222,19 @@ public class CPMController extends ControllerBase
     }
 
     /**
-     * Adds the project name, workunit id and name to an assignment. If any of these values are
-     * null in an assignment, update all three of the values.
-     * @param projectName the project name to add to the assignment
-     * @param workUnitID the workunit id to add to the assignment
-     * @param workUnitName the workunit name to add
-     * @param assignment the assignment everything will be added to
-     * @return the updated assignment
+     * Adds project name, workunitId and workunitName to an assignment.
+     *
+     * @param model the model to get details from.
+     * @param assignment the assignment to add details to.
      */
-    private Assignment addCPMdetails(final String projectName, final String workUnitID,
-                                     final String workUnitName, final Assignment assignment) {
-        if (assignment.getProjectName() == null
-                || assignment.getWorkUnitId() == null
-                || assignment.getWorkUnitName() == null) {
-
-            assignment.setProjectName(projectName);
-            assignment.setWorkUnitId(workUnitID);
-            assignment.setWorkUnitName(workUnitName);
-        }
-        return this.repositories.getAssignmentRepository().save(assignment);
+    private void addCPMDetails(final CPMDataModel model, final Assignment assignment) {
+        assignment.setProjectName(model.getProjectName());
+        assignment.setWorkUnitId(model.getWorkunitID());
+        assignment.setWorkUnitName(model.getWorkunitName());
+
     }
 
+
     /**
      * Gets the extension of a filename
      * @param filename the filename
diff --git a/core/src/main/java/nl/tudelft/ewi/auta/core/controller/ControllerBase.java b/core/src/main/java/nl/tudelft/ewi/auta/core/controller/ControllerBase.java
index 09d5a49edc5c31712a0476bfa4c6ff517e4ddfed..d71ff1e8be4baab062640e263607ee7c7d422352 100644
--- a/core/src/main/java/nl/tudelft/ewi/auta/core/controller/ControllerBase.java
+++ b/core/src/main/java/nl/tudelft/ewi/auta/core/controller/ControllerBase.java
@@ -1,10 +1,13 @@
 package nl.tudelft.ewi.auta.core.controller;
 
 import java.util.Map;
+import java.util.Objects;
 
 import nl.tudelft.ewi.auta.core.response.exception.FieldTypeMismatchException;
 import nl.tudelft.ewi.auta.core.response.exception.MissingFieldException;
+import org.springframework.context.support.DefaultMessageSourceResolvable;
 import org.springframework.http.ResponseEntity;
+import org.springframework.validation.BindingResult;
 
 /**
  * Provides utility functions to controllers.
@@ -65,6 +68,24 @@ public class ControllerBase {
         return this.getParam(req, key, Map.class);
     }
 
+    /**
+     * Checks if there are any results, and throws a field type mismatch if there are any.
+     *
+     * @param result the results of the validation.
+     * @throws FieldTypeMismatchException if there are any errors
+     */
+    protected void checkForErrors(final BindingResult result) {
+        if (result.hasErrors()) {
+            final var error = result.getAllErrors().stream()
+                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
+                    .filter(Objects::nonNull)
+                    .findFirst()
+                    .orElseThrow(FieldTypeMismatchException::new);
+            throw new FieldTypeMismatchException(error);
+        }
+    }
+
+
     /**
      * Returns a builder for a Not Found response entity.
      *
diff --git a/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CourseController.java b/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CourseController.java
index ed1186fc1ca754bedaf06146364583fbc3e550c5..dd2c7c1472e78f9d59e406a07e4afbd903a89e60 100644
--- a/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CourseController.java
+++ b/core/src/main/java/nl/tudelft/ewi/auta/core/controller/CourseController.java
@@ -6,14 +6,12 @@ import nl.tudelft.ewi.auta.core.model.Course;
 import nl.tudelft.ewi.auta.core.model.CourseValidator;
 import nl.tudelft.ewi.auta.core.response.Response;
 import nl.tudelft.ewi.auta.core.response.exception.CourseAlreadyExistsException;
-import nl.tudelft.ewi.auta.core.response.exception.FieldTypeMismatchException;
 import nl.tudelft.ewi.auta.core.response.exception.InvalidRoleException;
 import nl.tudelft.ewi.auta.core.response.exception.MisconfiguredUserException;
 import nl.tudelft.ewi.auta.core.response.exception.MissingUserException;
 import nl.tudelft.ewi.auta.core.response.exception.UserNotAuthorizedException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.springframework.context.support.DefaultMessageSourceResolvable;
 import org.springframework.http.ResponseEntity;
 import org.springframework.security.core.userdetails.User;
 import org.springframework.stereotype.Controller;
@@ -31,7 +29,6 @@ import org.springframework.web.bind.annotation.RequestBody;
 import java.sql.SQLException;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 
 
 /**
@@ -343,23 +340,4 @@ public class CourseController extends ControllerBase {
         }
         return this.securedService.addTA(course, username);
     }
-
-
-
-    /**
-     * Checks if there are any results, and throws a field type mismatch if there are any.
-     *
-     * @param result the results of the validation.
-     * @throws FieldTypeMismatchException if there are any errors
-     */
-    private void checkForErrors(final BindingResult result) {
-        if (result.hasErrors()) {
-            final var error = result.getAllErrors().stream()
-                    .map(DefaultMessageSourceResolvable::getDefaultMessage)
-                    .filter(Objects::nonNull)
-                    .findFirst()
-                    .orElseThrow(FieldTypeMismatchException::new);
-            throw new FieldTypeMismatchException(error);
-        }
-    }
 }
diff --git a/core/src/main/java/nl/tudelft/ewi/auta/core/model/CPMDataModel.java b/core/src/main/java/nl/tudelft/ewi/auta/core/model/CPMDataModel.java
new file mode 100644
index 0000000000000000000000000000000000000000..88d14ce07b068212c47d606a1ceb3bb2866c18a5
--- /dev/null
+++ b/core/src/main/java/nl/tudelft/ewi/auta/core/model/CPMDataModel.java
@@ -0,0 +1,190 @@
+package nl.tudelft.ewi.auta.core.model;
+
+import javax.annotation.Nullable;
+import javax.validation.constraints.NotBlank;
+
+/**
+ * The data model used by CPM.
+ */
+public class CPMDataModel {
+    // Annotating the fields with @NotBlank and initializing them to an empty string is not a
+    // mistake, hibernate validation checks if they are not blank after a request has been
+    // received.
+
+    /**
+     * The project name.
+     */
+    @NotBlank
+    private String projectName = "";
+
+    /**
+     * The workunit id.
+     */
+    @NotBlank
+    private String workunitID = "";
+
+    /**
+     * The workunit name.
+     */
+    @NotBlank
+    private String workunitName = "";
+
+    /**
+     * The member name.
+     */
+    @NotBlank
+    private String memberName = "";
+
+    /**
+     * Upload comments associated with the submission.
+     */
+    @Nullable
+    private String uploadComments;
+
+    /**
+     * The verification code.
+     */
+    @NotBlank
+    private String verificationCode = "";
+
+    /**
+     * The filename.
+     */
+    @NotBlank
+    private String filename = "";
+
+    /**
+     * Gets the project name.
+     *
+     * @return the project name
+     */
+    public String getProjectName() {
+        return this.projectName;
+    }
+
+    /**
+     * Sets the projectName.
+     *
+     * @param projectName the projectName to set
+     */
+    public void setProjectName(final String projectName) {
+        this.projectName = projectName;
+    }
+
+    /**
+     * Gets the workunit ID.
+     *
+     * @return the workunit id
+     */
+    public String getWorkunitID() {
+        return this.workunitID;
+    }
+
+    /**
+     * Sets the workunitID.
+     *
+     * @param workunitID the workunitID to set
+     */
+    public void setWorkunitID(final String workunitID) {
+        this.workunitID = workunitID;
+    }
+
+    /**
+     * Gets the workunit name.
+     *
+     * @return the workunit name
+     */
+    public String getWorkunitName() {
+        return this.workunitName;
+    }
+
+    /**
+     * Sets the workunitName.
+     *
+     * @param workunitName the workunitName to set
+     */
+    public void setWorkunitName(final String workunitName) {
+        this.workunitName = workunitName;
+    }
+
+    /**
+     * Gets the member name.
+     *
+     * @return the member name
+     */
+    public String getMemberName() {
+        return this.memberName;
+    }
+
+    /**
+     * Sets the memberName.
+     *
+     * @param memberName the memberName to set
+     */
+    public void setMemberName(final String memberName) {
+        this.memberName = memberName;
+    }
+
+    /**
+     * Gets the upload comments.
+     *
+     * @return the upload comments
+     */
+    @Nullable
+    public String getUploadComments() {
+        return this.uploadComments;
+    }
+
+    /**
+     * Sets the uploadComments.
+     *
+     * @param uploadComments the uploadComments to set
+     */
+    public void setUploadComments(final @Nullable  String uploadComments) {
+        this.uploadComments = uploadComments;
+    }
+
+    /**
+     * Gets the verification code.
+     *
+     * @return the verification code
+     */
+    public String getVerificationCode() {
+        return this.verificationCode;
+    }
+
+    /**
+     * Sets the verificationCode.
+     *
+     * @param verificationCode the verificationCode to set
+     */
+    public void setVerificationCode(final String verificationCode) {
+        this.verificationCode = verificationCode;
+    }
+
+    /**
+     * Gets the filename.
+     *
+     * @return the filename
+     */
+    public String getFilename() {
+        return this.filename;
+    }
+
+    /**
+     * Sets the filename.
+     *
+     * @param filename the filename to set
+     */
+    public void setFilename(final String filename) {
+        this.filename = filename;
+    }
+
+    /**
+     * Gets the name for a CPM submission.
+     * @return the submission name.
+     */
+    public String getSubmissionName() {
+        return String.format("%s:%s", this.projectName, this.workunitName);
+    }
+}
diff --git a/core/src/test/java/nl/tudelft/ewi/auta/core/controller/CPMControllerTest.java b/core/src/test/java/nl/tudelft/ewi/auta/core/controller/CPMControllerTest.java
index 160076062ce78adab736556e1f6a56bea44f533d..a8a6967c2cb9817b06fba343a683c606a4d39ad5 100644
--- a/core/src/test/java/nl/tudelft/ewi/auta/core/controller/CPMControllerTest.java
+++ b/core/src/test/java/nl/tudelft/ewi/auta/core/controller/CPMControllerTest.java
@@ -11,6 +11,7 @@ import nl.tudelft.ewi.auta.core.database.Repositories;
 import nl.tudelft.ewi.auta.core.database.SubmissionRepository;
 import nl.tudelft.ewi.auta.core.jobs.JobQueue;
 import nl.tudelft.ewi.auta.core.model.Assignment;
+import nl.tudelft.ewi.auta.core.model.CPMDataModel;
 import nl.tudelft.ewi.auta.core.model.FileStore;
 import nl.tudelft.ewi.auta.core.model.Submission;
 import nl.tudelft.ewi.auta.core.report.CpmReportGenerator;
@@ -26,9 +27,11 @@ import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
 import org.mockito.stubbing.Answer;
+import org.springframework.http.HttpStatus;
 import org.springframework.mock.web.MockMultipartFile;
 import org.springframework.test.web.servlet.MockMvc;
 import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+import org.springframework.validation.BindingResult;
 
 import java.io.IOException;
 import java.net.URL;
@@ -67,6 +70,8 @@ public class CPMControllerTest {
     @Mock
     private EntityRepository entityRepository;
 
+    @Mock
+    private BindingResult bindingResult;
 
     @Mock
     private Repositories repositories;
@@ -91,6 +96,8 @@ public class CPMControllerTest {
 
     private static final String CPM_URL = "http://auta.f00f.nl/";
 
+    private CPMDataModel model;
+
 
     @InjectMocks
     private CPMController controller;
@@ -125,7 +132,7 @@ public class CPMControllerTest {
 
         MockitoAnnotations.initMocks(this);
         Mockito.when(this.generator.generateReport(Mockito.any())).thenReturn("report");
-
+        Mockito.when(this.bindingResult.hasErrors()).thenReturn(false);
         Mockito.when(this.repositories.getAssignmentRepository()).thenReturn(this.assignmentStore);
         Mockito.when(this.repositories.getSubmissionRepository()).thenReturn(this.submissionStore);
         Mockito.when(this.repositories.getIdentityRepository()).thenReturn(this.identityRepository);
@@ -136,7 +143,7 @@ public class CPMControllerTest {
             assignment1.setId(AID);
             return assignment1;
         };
-
+        this.model = this.initializeModel();
         Mockito.when(this.entityRepository.findByParentIds(eq(SID), eq(AID))).thenReturn(
                 Optional.of(this.container));
 
@@ -163,126 +170,53 @@ public class CPMControllerTest {
 
         Mockito.when(this.settings.get(Mockito.any())).thenReturn(CPM_URL);
         Mockito.when(this.settings.get(Mockito.any(), Mockito.any())).thenReturn(CPM_URL);
-
-
         this.mvc = MockMvcBuilders.standaloneSetup(this.controller).build();
     }
 
-    @Test
-    public void cpmRequestTest() throws IOException {
-        var newSubmission = new Submission(PROJECT_NAME + ":" + WORKUNIT_NAME, FILENAME, AID);
-        newSubmission.setCpmVerificationCode(VERIFICATION_CODE);
-        newSubmission.setAssignmentId(AID);
-        newSubmission.setId(SID);
-
-        var multipartFile = new MockMultipartFile("filename", "contents".getBytes());
-        this.controller.uploadAction(multipartFile,
-                PROJECT_NAME,
-                WORKUNIT_ID,
-                WORKUNIT_NAME,
-                MEMBER_NAME,
-                "comments",
-                VERIFICATION_CODE,
-                FILENAME,
-                AID);
-        Mockito.verify(this.assignmentStore, Mockito.atLeastOnce()).findById(eq(AID));
-        Mockito.verify(this.submissionStore, Mockito.atLeastOnce())
-                .save(Mockito.argThat(new SubmissionMatcher(newSubmission)));
+    private CPMDataModel initializeModel() {
+        final var newModel = new CPMDataModel();
+        newModel.setProjectName(PROJECT_NAME);
+        newModel.setWorkunitID(WORKUNIT_ID);
+        newModel.setWorkunitName(WORKUNIT_NAME);
+        newModel.setUploadComments("comments");
+        newModel.setVerificationCode(VERIFICATION_CODE);
+        newModel.setFilename(FILENAME);
+        return newModel;
     }
 
     @Test
-    public void cpmRequestEmptyFilenames() throws IOException {
+    public void cpmRequestTest() throws IOException {
         var newSubmission = new Submission(PROJECT_NAME + ":" + WORKUNIT_NAME, FILENAME, AID);
         newSubmission.setCpmVerificationCode(VERIFICATION_CODE);
         newSubmission.setAssignmentId(AID);
         newSubmission.setId(SID);
 
-        var multipartFile = new MockMultipartFile("filename", "contents".getBytes());
-        this.controller.uploadAction(multipartFile,
-                PROJECT_NAME,
-                WORKUNIT_ID,
-                WORKUNIT_NAME,
-                MEMBER_NAME,
-                "comments",
-                VERIFICATION_CODE,
-                "",
-                AID);
-        Mockito.verify(this.assignmentStore, Mockito.atLeastOnce()).findById(eq(AID));
-        Mockito.verify(this.fileStore, Mockito.atLeastOnce()).add(eq(""),
-                Mockito.any());
-        Mockito.verify(this.submissionStore, Mockito.atLeastOnce())
-                .save(Mockito.argThat(new SubmissionMatcher(newSubmission)));
-    }
+        var newAssignment = new Assignment("ASSIGNMENT_NAME");
+        newAssignment.setId(AID);
+        newAssignment.setWorkUnitName(WORKUNIT_NAME);
+        newAssignment.setProjectName(PROJECT_NAME);
+        newAssignment.setWorkUnitId(WORKUNIT_ID);
 
-    @Test
-    public void cpmRequestInvalidFilenames() throws IOException {
-        var newSubmission = new Submission(PROJECT_NAME + ":" + WORKUNIT_NAME, FILENAME, AID);
-        newSubmission.setCpmVerificationCode(VERIFICATION_CODE);
-        newSubmission.setAssignmentId(AID);
-        newSubmission.setId(SID);
+        Mockito.when(this.assignmentStore.findById(eq(AID))).thenReturn(
+                Optional.of(this.assignment));
 
         var multipartFile = new MockMultipartFile("filename", "contents".getBytes());
         this.controller.uploadAction(multipartFile,
-                PROJECT_NAME,
-                WORKUNIT_ID,
-                WORKUNIT_NAME,
-                MEMBER_NAME,
-                "comments",
-                VERIFICATION_CODE,
-                "/awda/awdacv/.zip.zip.java.",
-                AID);
+                AID, this.model, this.bindingResult);
         Mockito.verify(this.assignmentStore, Mockito.atLeastOnce()).findById(eq(AID));
-        Mockito.verify(this.fileStore, Mockito.atLeastOnce()).add(
-                eq("/awda/awdacv/.zip.zip.java."), Mockito.any());
+        Mockito.verify(this.assignmentStore, Mockito.atLeastOnce())
+                .save(Mockito.argThat(new AssignmentMatcher(newAssignment)));
         Mockito.verify(this.submissionStore, Mockito.atLeastOnce())
                 .save(Mockito.argThat(new SubmissionMatcher(newSubmission)));
     }
 
-
     @Test
     public void cpmInvalidAidTest() throws IOException {
         var multipartFile = new MockMultipartFile("filename", "contents".getBytes());
-        var response = this.controller.uploadAction(multipartFile,
-                PROJECT_NAME,
-                WORKUNIT_ID,
-                WORKUNIT_NAME,
-                MEMBER_NAME,
-                "comments",
-                VERIFICATION_CODE,
-                FILENAME,
-                "invalid_aid");
-
-        assertThat(response.getStatusCodeValue()).isEqualTo(404);
-    }
-
-    @Test
-    public void cpmRequestAssignmentWithoutCPMDetailsTest() throws IOException {
-        this.assignment = new Assignment("ASSIGNMENT_NAME");
-        this.assignment.setId(AID);
-
-        Mockito.when(this.assignmentStore.findById(eq(AID))).thenReturn(
-                Optional.of(this.assignment));
-
-        var multipartFile = new MockMultipartFile("filename", "contents".getBytes());
-        this.controller.uploadAction(multipartFile,
-                PROJECT_NAME,
-                WORKUNIT_ID,
-                WORKUNIT_NAME,
-                MEMBER_NAME,
-                "comments",
-                VERIFICATION_CODE,
-                FILENAME,
-                AID);
+        var response = this.controller.uploadAction(multipartFile, "INVALID_AID", this.model,
+                this.bindingResult);
 
-        var newAssignment = new Assignment("ASSIGNMENT_NAME");
-        newAssignment.setId(AID);
-        newAssignment.setWorkUnitName(WORKUNIT_NAME);
-        newAssignment.setProjectName(PROJECT_NAME);
-        newAssignment.setWorkUnitId(WORKUNIT_ID);
-
-        Mockito.verify(this.assignmentStore, Mockito.atLeastOnce()).findById(eq(AID));
-        Mockito.verify(this.assignmentStore, Mockito.atLeastOnce())
-                .save(Mockito.argThat(new AssignmentMatcher(newAssignment)));
+        assertThat(response.getStatusCodeValue()).isEqualTo(HttpStatus.NOT_FOUND.value());
     }
 
     public class AssignmentMatcher implements ArgumentMatcher<Assignment> {
diff --git a/src/main/java/nl/tudelft/ewi/auta/common/model/PyLintResult.java b/src/main/java/nl/tudelft/ewi/auta/common/model/PyLintResult.java
index 7c851cf1dc230dd282b28e5c3d13e8d477b18dbf..3a5363bbc47fbf293b77a57e73408e358e69b2cf 100644
--- a/src/main/java/nl/tudelft/ewi/auta/common/model/PyLintResult.java
+++ b/src/main/java/nl/tudelft/ewi/auta/common/model/PyLintResult.java
@@ -50,6 +50,22 @@ public class PyLintResult {
      */
     private String messageid;
 
+    /**
+     * Creates a new empty PyLintResult.
+     */
+    public PyLintResult() {
+        this.type = "";
+        this.module = "";
+        this.obj = "";
+        this.line = "";
+        this.column = "";
+        this.path = "";
+        this.symbol = "";
+        this.message = "";
+        this.messageid = "";
+    }
+
+
     /**
      * Retrieves the type of the feedback.
      * @return the feedback type
@@ -123,28 +139,83 @@ public class PyLintResult {
     }
 
     /**
-     * Creates a new PyLintResult.
-     * @param type the type of the feedback
-     * @param module the module of the feedback
-     * @param obj the object of the feedback
-     * @param line the line of the feedback
-     * @param column the column of the feedback
-     * @param path the path of the feedback
-     * @param symbol the symbol of the feedback
-     * @param message the message of the feedback
-     * @param messageid the message id of the feedback
+     * Sets the type.
+     *
+     * @param type the type to set
      */
-    public PyLintResult(final String type, final String module, final String obj,
-                        final String line, final String column, final String path,
-                        final String symbol, final String message, final String messageid) {
+    public void setType(final String type) {
         this.type = type;
+    }
+
+    /**
+     * Sets the module name the feedback is from.
+     *
+     * @param module the module to set
+     */
+    public void setModule(final String module) {
         this.module = module;
+    }
+
+    /**
+     * Sets the class and/or method/function the feedback is from..
+     *
+     * @param obj class, method or function the feedback is from
+     */
+    public void setObj(final String obj) {
         this.obj = obj;
+    }
+
+    /**
+     * Sets the line the feedback refers to.
+     *
+     * @param line the line to set
+     */
+    public void setLine(final String line) {
         this.line = line;
+    }
+
+    /**
+     * Sets the column the feedback refers to.
+     *
+     * @param column the column to set
+     */
+    public void setColumn(final String column) {
         this.column = column;
+    }
+
+    /**
+     * Sets the path to the file the feedback was generated from.
+     *
+     * @param path the path to set
+     */
+    public void setPath(final String path) {
         this.path = path;
+    }
+
+    /**
+     * Sets the symbol the feedback is saved under.
+     *
+     * @param symbol the symbol to set
+     */
+    public void setSymbol(final String symbol) {
         this.symbol = symbol;
+    }
+
+    /**
+     * Sets the message that is associated with the feedback.
+     *
+     * @param message the message to set
+     */
+    public void setMessage(final String message) {
         this.message = message;
+    }
+
+    /**
+     * Sets the id of the message the feedback refers to.
+     *
+     * @param messageid the messageid to set
+     */
+    public void setMessageid(final String messageid) {
         this.messageid = messageid;
     }
 }
diff --git a/src/test/java/nl/tudelft/ewi/auta/common/model/PyLintResultTest.java b/src/test/java/nl/tudelft/ewi/auta/common/model/PyLintResultTest.java
index 202897067f84639abc8434b37cf032db40222fea..deb830e778f6f3b1fc87eba34cab03ce04016d00 100644
--- a/src/test/java/nl/tudelft/ewi/auta/common/model/PyLintResultTest.java
+++ b/src/test/java/nl/tudelft/ewi/auta/common/model/PyLintResultTest.java
@@ -8,8 +8,16 @@ public class PyLintResultTest {
 
     @Test
     public void testConstructor() {
-        final var result = new PyLintResult("type", "module", "obj", "line",
-                "column", "path", "symbol", "message", "messageid");
+        final var result = new PyLintResult();
+        result.setType("type");
+        result.setModule("module");
+        result.setObj("obj");
+        result.setLine("line");
+        result.setColumn("column");
+        result.setPath("path");
+        result.setSymbol("symbol");
+        result.setMessage("message");
+        result.setMessageid("messageid");
         assertThat(result.getType()).isEqualTo("type");
         assertThat(result.getModule()).isEqualTo("module");
         assertThat(result.getObject()).isEqualTo("obj");
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/JobAnalyzer.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/JobAnalyzer.java
index 8ab02d41fa467a991541f9fe26981d070de51b62..b26994a00ace7dab24042c93213f210770834318 100644
--- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/JobAnalyzer.java
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/JobAnalyzer.java
@@ -2,6 +2,9 @@ package nl.tudelft.ewi.auta.checker;
 
 import nl.tudelft.ewi.auta.worker.Job;
 
+import java.util.List;
+import java.util.stream.Collectors;
+
 /**
  * An analyzer for submissions.
  */
@@ -10,4 +13,16 @@ public abstract class JobAnalyzer implements Analyzer<Job> {
     public Class<Job> getType() {
         return Job.class;
     }
+
+    /**
+     * Gets a list of all filenames.
+     *
+     * @param job the job to get the filenames from
+     * @return a list of filenames
+     */
+    public List<String> getFileNames(final Job job) {
+        return job.getProject().getFiles().stream()
+                .map(file -> file.getAbsolutePath().toString())
+                .collect(Collectors.toList());
+    }
 }
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/CPDChecker.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/CPDChecker.java
index 1da1b158c37448efe0e522235d5888d6fda5559f..a5fdfaba8da593a11caa76645527aaf3997fdf18 100644
--- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/CPDChecker.java
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/CPDChecker.java
@@ -19,7 +19,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
-import java.util.stream.Collectors;
 
 /**
  * A checker that uses the CPD tool (Copy Paste Detector) included in the PMD suite to find
@@ -72,9 +71,7 @@ public class CPDChecker extends JobAnalyzer {
 
         //Creates a file that contains a list of paths to files that are to be analyzed by CPD.
         var fileListPath = Files.createTempFile(this.workerSettings.getTemp(), "stream", ".csv");
-        var fragmentPaths = victim.getProject().getFiles().stream()
-                .map(f -> f.getAbsolutePath().toString())
-                .collect(Collectors.toList());
+        var fragmentPaths = this.getFileNames(victim);
         try (var fileWriter = new FileWriter(fileListPath.toFile(), StandardCharsets.UTF_8)) {
             fileWriter.write(String.join(",", fragmentPaths));
         }
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/Lizard.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/Lizard.java
index 7ba43c256304053807eb90b50b093342aede8042..f277a850d50dc89b86a9cf72d2db0681ec68a86d 100644
--- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/Lizard.java
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/generic/Lizard.java
@@ -104,7 +104,7 @@ public class Lizard extends EntityAnalyzer {
     /**
      * A pattern matching paamayim nekudotayim.
      */
-    private static final Pattern PAAMAYIM_NEKUDOTAYIM = Pattern.compile("::");
+    private static final Pattern DOUBLE_COLON = Pattern.compile("::");
 
     /**
      * The index of LOC metrics in the XML.
@@ -298,7 +298,7 @@ public class Lizard extends EntityAnalyzer {
             }
 
             final var sep = NAMESPACE_SEPARATORS.getOrDefault(language, DEFAULT_SEPARATOR);
-            final var namespace = PAAMAYIM_NEKUDOTAYIM
+            final var namespace = DOUBLE_COLON
                                           .matcher(this.getAttribute(item, "namespace"))
                                           .replaceAll(sep);
             final var numParameters = Integer.parseInt(this.getAttribute(item, "param-count"));
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/CKAnalyzer.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/CKAnalyzer.java
index 83a263d4b9d0d8ae5e46bcdae3544f55297a290a..a92afd2c8200a73a418e67cce92bbc9b57294487 100644
--- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/CKAnalyzer.java
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/CKAnalyzer.java
@@ -28,6 +28,7 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.function.Supplier;
 
 public class CKAnalyzer extends EntityAnalyzer {
 
@@ -176,67 +177,68 @@ public class CKAnalyzer extends EntityAnalyzer {
     private Entity getClassLevelMetrics(final CKClassResult rc, final Entity fileEntity) {
         final var ce = new Entity(fileEntity, rc.getClassName().substring(
                 rc.getClassName().lastIndexOf(".") + 1), false, EntityLevel.CLASS);
-        ce.addMetric(new IntegerMetric(rc.getReturnQty(), MetricName.RETURN_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getLoopQty(), MetricName.LOOP_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getComparisonsQty(), MetricName.COMPARISON_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getTryCatchQty(), MetricName.TRY_CATCH_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getParenthesizedExpsQty(),
-                MetricName.PARENTHESIZED_EXPRESSION_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getStringLiteralsQty(), MetricName.STRING_LITERAL_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumbersQty(), MetricName.NUMBER_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getAssignmentsQty(), MetricName.ASSIGNMENTS_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getMathOperationsQty(),
-                MetricName.MATH_OPERATIONS_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getVariablesQty(), MetricName.VARIABLES_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getMaxNestedBlocks(), MetricName.MAX_NESTED_BLOCKS));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfAbstractMethods(),
-                MetricName.ANONYMOUS_CLASSES_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getSubClassesQty(), MetricName.SUBCLASSES_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getLambdasQty(), MetricName.LAMBDAS_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getUniqueWordsQty(), MetricName.UNIQUE_WORDS_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfMethods(), MetricName.METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfStaticMethods(),
-                MetricName.STATIC_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfPublicMethods(),
-                MetricName.PUBLIC_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfPrivateMethods(),
-                MetricName.PRIVATE_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfProtectedMethods(),
-                MetricName.PROTECTED_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfDefaultMethods(),
-                MetricName.DEFAULT_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfAbstractMethods(),
-                MetricName.ABSTRACT_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfFinalMethods(),
-                MetricName.FINAL_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfSynchronizedMethods(),
-                MetricName.SYNCHRONIZED_METHOD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfFields(), MetricName.FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfStaticFields(),
-                MetricName.STATIC_FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfPublicFields(),
-                MetricName.PUBLIC_FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfPrivateFields(),
-                MetricName.PRIVATE_FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfProtectedFields(),
-                MetricName.PROTECTED_FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfDefaultFields(),
-                MetricName.DEFAULT_FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfFinalFields(), MetricName.FINAL_FIELD_COUNT));
-        ce.addMetric(new IntegerMetric(rc.getNumberOfSynchronizedFields(),
-                MetricName.SYNCHRONIZED_FIELD_COUNT));
 
+        this.addIntegerMetric(ce, rc::getReturnQty, MetricName.RETURN_COUNT);
+        this.addIntegerMetric(ce, rc::getLoopQty, MetricName.LOOP_COUNT);
+        this.addIntegerMetric(ce, rc::getComparisonsQty, MetricName.COMPARISON_COUNT);
+        this.addIntegerMetric(ce, rc::getTryCatchQty, MetricName.TRY_CATCH_COUNT);
+        this.addIntegerMetric(ce, rc::getStringLiteralsQty, MetricName.STRING_LITERAL_COUNT);
+        this.addIntegerMetric(ce, rc::getNumbersQty, MetricName.NUMBER_COUNT);
+        this.addIntegerMetric(ce, rc::getAssignmentsQty, MetricName.ASSIGNMENTS_COUNT);
+        this.addIntegerMetric(ce, rc::getMathOperationsQty, MetricName.MATH_OPERATIONS_COUNT);
+        this.addIntegerMetric(ce, rc::getVariablesQty, MetricName.VARIABLES_COUNT);
+        this.addIntegerMetric(ce, rc::getMaxNestedBlocks, MetricName.MAX_NESTED_BLOCKS);
+        this.addIntegerMetric(ce, rc::getSubClassesQty, MetricName.SUBCLASSES_COUNT);
+        this.addIntegerMetric(ce, rc::getLambdasQty, MetricName.LAMBDAS_COUNT);
+        this.addIntegerMetric(ce, rc::getUniqueWordsQty, MetricName.UNIQUE_WORDS_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfMethods, MetricName.METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfStaticMethods, MetricName.STATIC_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfPublicMethods, MetricName.PUBLIC_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfPrivateMethods, MetricName.PRIVATE_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfDefaultMethods, MetricName.DEFAULT_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfAbstractMethods, MetricName.ABSTRACT_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfFinalMethods, MetricName.FINAL_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfFields, MetricName.FIELD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfStaticFields, MetricName.STATIC_FIELD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfPublicFields, MetricName.PUBLIC_FIELD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfPrivateFields, MetricName.PRIVATE_FIELD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfProtectedFields, MetricName.PROTECTED_FIELD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfDefaultFields, MetricName.DEFAULT_FIELD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfFinalFields, MetricName.FINAL_FIELD_COUNT);
 
-        ce.addMetric(new IntegerMetric(rc.getLoc(), MetricName.LINES_OF_CODE));
-        ce.addMetric(new IntegerMetric(rc.getNosi(), MetricName.NUMBER_OF_STATIC_INVOCATIONS));
-        ce.addMetric(new IntegerMetric(rc.getRfc(), MetricName.RESPONSE_FOR_A_CLASS));
-        ce.addMetric(new IntegerMetric(rc.getLcom(), MetricName.LACK_OF_COHESION_OF_METHODS));
-        ce.addMetric(new IntegerMetric(rc.getCbo(), MetricName.COUPLING_BETWEEN_OBJECTS));
-        ce.addMetric(new IntegerMetric(rc.getWmc(), MetricName.WEIGHT_METHOD_CLASS));
-        ce.addMetric(new IntegerMetric(rc.getDit(), MetricName.DEPTH_INHERITANCE_TREE));
+        this.addIntegerMetric(ce, rc::getParenthesizedExpsQty,
+                MetricName.PARENTHESIZED_EXPRESSION_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfAbstractMethods,
+                MetricName.ANONYMOUS_CLASSES_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfProtectedMethods,
+                MetricName.PROTECTED_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfSynchronizedMethods,
+                MetricName.SYNCHRONIZED_METHOD_COUNT);
+        this.addIntegerMetric(ce, rc::getNumberOfSynchronizedFields,
+                MetricName.SYNCHRONIZED_FIELD_COUNT);
+
+        this.addIntegerMetric(ce, rc::getLoc, MetricName.LINES_OF_CODE);
+        this.addIntegerMetric(ce, rc::getNosi, MetricName.NUMBER_OF_STATIC_INVOCATIONS);
+        this.addIntegerMetric(ce, rc::getRfc, MetricName.RESPONSE_FOR_A_CLASS);
+        this.addIntegerMetric(ce, rc::getLcom, MetricName.LACK_OF_COHESION_OF_METHODS);
+        this.addIntegerMetric(ce, rc::getCbo, MetricName.COUPLING_BETWEEN_OBJECTS);
+        this.addIntegerMetric(ce, rc::getWmc, MetricName.WEIGHT_METHOD_CLASS);
+        this.addIntegerMetric(ce, rc::getDit, MetricName.DEPTH_INHERITANCE_TREE);
         return ce;
     }
 
+    /**
+     * Adds an integer metric to an entity.
+     *
+     * @param entity the entity to add the metric to
+     * @param supplier the function that will be called that supplies an integer
+     * @param metricName the metric name
+     */
+    private void addIntegerMetric(final Entity entity, final Supplier<Integer> supplier,
+                                  final MetricName metricName) {
+        entity.addMetric(new IntegerMetric(supplier.get(), metricName));
+    }
+
     /**
      * Creates a mapping of the method name to method level metrics.
      * @param resultClass the class that contains the results
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/ClassAndMethodAnalyticsJava.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/ClassAndMethodAnalyticsJava.java
index b1470ade8501e12c613f89f3e87c9e3d67275dab..0128eb6e6445982f8ca6d5e6c030a130c4c51da3 100644
--- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/ClassAndMethodAnalyticsJava.java
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/java/ClassAndMethodAnalyticsJava.java
@@ -187,7 +187,13 @@ public class ClassAndMethodAnalyticsJava extends EntityAnalyzer {
         ));
 
         map.put(MetricName.PARAMETER_COUNT, Collections.singletonList(
-                this.mapIntWarnFailMax("has many parameters", "has too many parameters", 8, 12)
+                this.numberWarnFailMax("has many parameters", "has too many parameters", 7, 10,
+                        "A high parameter count often means that the method contains too much "
+                                + "logic. This can be solved by either splitting the method and "
+                                + "thus reducing the amount of parameters or creating a new class "
+                                + "which has some of the parameters in it and than simply pass "
+                                + "that class as parameter."
+                        )
         ));
 
         return map;
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java
index f86b2686949de15c348d5a40c410a05af2e119f0..b40392180b53a7f717b0f8445248e071ee11d94c 100644
--- a/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLint.java
@@ -20,11 +20,8 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import java.io.IOException;
-import java.nio.file.DirectoryStream;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.Paths;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -98,17 +95,8 @@ public class PyLint extends JobAnalyzer {
             justification = "SpotBugs is not aware of try-with-resources and its null checks"
     )
     public void analyze(final Job victim, final Object options) throws IOException {
-        final var analyzePath = victim.getDir().toAbsolutePath();
 
-        //final var fileList = analyzePath.toFile().listFiles();
-
-        final var fileNames = new ArrayList<String>();
-        try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(
-                Paths.get(analyzePath.toAbsolutePath().toString()))) {
-            for (Path path : directoryStream) {
-                fileNames.add(path.toString());
-            }
-        }
+        final var fileNames = this.getFileNames(victim);
 
         // For every module
         for (var i = 0; i < fileNames.size(); i++) {
@@ -146,7 +134,9 @@ public class PyLint extends JobAnalyzer {
 
             // Retrieve the JSon object from the process
             final var pyLintResults = new HashSet<PyLintResult>();
-            var gson = new GsonBuilder().create();
+            var gsonBuilder = new GsonBuilder();
+            gsonBuilder.registerTypeAdapter(PyLintResult.class, new PyLintOutputDeserializer());
+            final var gson = gsonBuilder.create();
             try (var reader = Files.newBufferedReader(tempOutputFile);
                  var jsonReader = gson.newJsonReader(reader)) {
                 jsonReader.beginArray();
diff --git a/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLintOutputDeserializer.java b/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLintOutputDeserializer.java
new file mode 100644
index 0000000000000000000000000000000000000000..ad8039b066042a6297bade03664c67c7f617e6be
--- /dev/null
+++ b/worker/src/main/java/nl/tudelft/ewi/auta/checker/python/PyLintOutputDeserializer.java
@@ -0,0 +1,50 @@
+package nl.tudelft.ewi.auta.checker.python;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import nl.tudelft.ewi.auta.common.model.PyLintResult;
+
+import java.lang.reflect.Type;
+
+/**
+ * A custom deserializer for a pylint results object. This is required to set the messageid field
+ * in the PyLintResult object, as Pylint stores this field as 'message-id' which cannot be a
+ * variable name in Java.
+ */
+public class PyLintOutputDeserializer implements JsonDeserializer<PyLintResult> {
+    /**
+     * Gson invokes this call-back method during deserialization when it encounters a field of the
+     * specified type.
+     * <p>In the implementation of this call-back method, you should consider invoking
+     * {@link JsonDeserializationContext#deserialize(JsonElement, Type)} method to create objects
+     * for any non-trivial field of the returned object. However, you should never invoke it on the
+     * the same type passing {@code json} since that will cause an infinite loop (Gson will call
+     * your
+     * call-back method again).
+     *
+     * @param json    The Json data being deserialized
+     * @param typeOfT The type of the Object to deserialize to
+     * @param context the deserialization context.
+     * @return a deserialized object of the specified type typeOfT which is a subclass of {@code T}
+     *
+     * @throws com.google.gson.JsonParseException if json is not in the expected format
+     *      of {@code typeofT}
+     */
+    @Override
+    public PyLintResult deserialize(final JsonElement json, final Type typeOfT,
+                                    final JsonDeserializationContext context) {
+        final var jsonObject = json.getAsJsonObject();
+        final var result = new PyLintResult();
+        result.setType(jsonObject.get("type").getAsString());
+        result.setModule(jsonObject.get("module").getAsString());
+        result.setObj(jsonObject.get("obj").getAsString());
+        result.setLine(jsonObject.get("line").getAsString());
+        result.setColumn(jsonObject.get("column").getAsString());
+        result.setPath(jsonObject.get("path").getAsString());
+        result.setSymbol(jsonObject.get("symbol").getAsString());
+        result.setMessage(jsonObject.get("message").getAsString());
+        result.setMessageid(jsonObject.get("message-id").getAsString());
+        return result;
+    }
+}
diff --git a/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java b/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java
index d9ee7e02c3b88a7f5e195f4c4857790ac9a00421..a6656dcae1f228f1efd47b9095b691ae102033c2 100644
--- a/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java
+++ b/worker/src/test/java/nl/tudelft/ewi/auta/checker/python/PyLintTest.java
@@ -1,7 +1,6 @@
 package nl.tudelft.ewi.auta.checker.python;
 
 import nl.tudelft.ewi.auta.Ob;
-import nl.tudelft.ewi.auta.checker.java.TestSmellsAnalyzer;
 import nl.tudelft.ewi.auta.common.model.entity.Entity;
 import nl.tudelft.ewi.auta.common.model.entity.EntityLevel;
 import nl.tudelft.ewi.auta.common.model.entity.FileEntity;
@@ -40,7 +39,7 @@ public class PyLintTest {
 
         final var python = new Python(new PythonDetector().findPython());
 
-        try (var in = TestSmellsAnalyzer.class
+        try (var in = PyLintTest.class
                 .getResourceAsStream("/lizard.zip");
              var out = Files.newOutputStream(zipPath)) {
             IOUtils.copy(in, out);