From 04ebea943e02a1b93ae8f5a70c31038dfd604144 Mon Sep 17 00:00:00 2001
From: srosse <stephane.rosse@frentix.com>
Date: Thu, 23 May 2019 13:51:17 +0200
Subject: [PATCH] OO-3914: backport bulk assessment with visibility and status

---
 .../bulk/BulkAssessment_2_DatasStep.java      |  2 +-
 .../course/assessment/bulk/DataStepForm.java  | 85 +++++++++++++++----
 .../bulk/_i18n/LocalStrings_de.properties     | 17 ++--
 .../bulk/_i18n/LocalStrings_en.properties     | 61 +++----------
 .../manager/BulkAssessmentTask.java           | 63 ++++++++++----
 .../assessment/model/BulkAssessmentDatas.java | 30 +++++++
 .../org/olat/course/nodes/gta/GTAManager.java |  2 +-
 .../nodes/gta/manager/GTAManagerImpl.java     |  4 +-
 .../nodes/gta/ui/GTACoachController.java      | 21 +++--
 ...CoachRevisionAndCorrectionsController.java |  2 +-
 10 files changed, 185 insertions(+), 102 deletions(-)

diff --git a/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_2_DatasStep.java b/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_2_DatasStep.java
index 7f6a36ad7d3..c638a3142da 100644
--- a/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_2_DatasStep.java
+++ b/src/main/java/org/olat/course/assessment/bulk/BulkAssessment_2_DatasStep.java
@@ -70,7 +70,7 @@ public class BulkAssessment_2_DatasStep extends BasicStep {
 			setNextStep(new BulkAssessment_2b_ChooseColumnsStep(ureq, savedDatas.getColumnsSettings()));
 		}
 		
-		hasPreviousStep = (courseNode == null ? false : true);
+		hasPreviousStep = courseNode != null;
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/assessment/bulk/DataStepForm.java b/src/main/java/org/olat/course/assessment/bulk/DataStepForm.java
index 2a43b317037..652c76a83a3 100644
--- a/src/main/java/org/olat/course/assessment/bulk/DataStepForm.java
+++ b/src/main/java/org/olat/course/assessment/bulk/DataStepForm.java
@@ -21,7 +21,6 @@ package org.olat.course.assessment.bulk;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -62,6 +61,8 @@ import org.olat.course.assessment.model.BulkAssessmentDatas;
 import org.olat.course.assessment.model.BulkAssessmentRow;
 import org.olat.course.assessment.model.BulkAssessmentSettings;
 import org.olat.course.nodes.AssessableCourseNode;
+import org.olat.course.nodes.GTACourseNode;
+import org.olat.modules.assessment.model.AssessmentEntryStatus;
 
 /**
  *
@@ -72,10 +73,16 @@ import org.olat.course.nodes.AssessableCourseNode;
 public class DataStepForm extends StepFormBasicController {
 
 	private static final String[] keys = new String[] {"tab","comma"};
+	private static final String[] statusKeys = new String[] { AssessmentEntryStatus.done.name(), AssessmentEntryStatus.inReview.name(), "not" };
+	private static final String[] visibilityKeys = new String[] { "visible", "notvisible", "notchanged" };
+	private static final String[] submissionKeys = new String[] { "accept", "notchanged" };
 
 	private TextElement dataEl;
 	private FileElement returnFileEl;
 	private SingleSelection delimiter;
+	private SingleSelection statusEl;
+	private SingleSelection visibilityEl;
+	private SingleSelection acceptSubmissionEl;
 
 	private OlatRootFileImpl targetArchive;
 	private BulkAssessmentDatas savedDatas;
@@ -133,13 +140,35 @@ public class DataStepForm extends StepFormBasicController {
 		String[] values = new String[] {translate("form.step3.delimiter.tab"),translate("form.step3.delimiter.comma")};
 		delimiter = uifactory.addRadiosVertical("delimiter", "form.step3.delimiter", formLayout, keys, values);
 		// preset delimiter type to first appearance of either tab or comma when data is available, default to tab for no data
-		int firstComma = dataVal.indexOf(",");
-		int firstTab = dataVal.indexOf("\t");
+		int firstComma = dataVal.indexOf(',');
+		int firstTab = dataVal.indexOf('\t');
 		if (firstComma > -1 && (firstTab == -1 || firstTab > firstComma )) {
 			delimiter.select("comma", true);
 		} else {
 			delimiter.select("tab", true);
 		}
+		
+		String[] statusValues = new String[] {
+				translate("form.step3.status.assessed"), translate("form.step3.status.review"),
+				translate("form.step3.status.dont.change")
+		};
+		statusEl = uifactory.addRadiosVertical("form.step3.status", "form.step3.status", formLayout, statusKeys, statusValues);
+		statusEl.select(statusKeys[statusKeys.length - 1], true);
+		String[] visibilityValues = new String[] {
+				translate("form.step3.visibility.visible"), translate("form.step3.visibility.notvisible"),
+				translate("form.step3.visibility.dont.change")
+		};
+		visibilityEl = uifactory.addRadiosVertical("form.step3.visibility", "form.step3.visibility", formLayout, visibilityKeys, visibilityValues);
+		visibilityEl.select(visibilityKeys[visibilityKeys.length - 1], true);
+		
+		if(courseNode instanceof GTACourseNode) {
+			String[] submissionValues = new String[] {
+				translate("form.step3.submission.accept"), translate("form.step3.submission.dont.change")
+			};
+			acceptSubmissionEl = uifactory.addRadiosVertical("form.step3.submission", "form.step3.submission", formLayout, submissionKeys, submissionValues);
+			acceptSubmissionEl.select(submissionKeys[submissionKeys.length - 1], true);
+			acceptSubmissionEl.setHelpTextKey("form.step3.submission.help", null);
+		}
 
 		// hide data input field in case the element does not have any score, passed or comment field enabled
 		if (onlyReturnFiles) {
@@ -180,6 +209,26 @@ public class DataStepForm extends StepFormBasicController {
 		if(datas == null) {
 			datas = new BulkAssessmentDatas();
 		}
+		
+		if(statusEl.isOneSelected()) {
+			String selectedStatus = statusEl.getSelectedKey();
+			if(AssessmentEntryStatus.isValueOf(selectedStatus)) {
+				datas.setStatus(AssessmentEntryStatus.valueOf(selectedStatus));
+			}
+		}
+		
+		if(visibilityEl.isOneSelected()) {
+			String selectedVisibility = visibilityEl.getSelectedKey();
+			if("visible".equals(selectedVisibility)) {
+				datas.setVisibility(Boolean.TRUE);
+			} else if("notvisible".equals(selectedVisibility)) {
+				datas.setVisibility(Boolean.FALSE);
+			}
+		}
+		
+		if(acceptSubmissionEl != null && acceptSubmissionEl.isOneSelected()) {
+			datas.setAcceptSubmission(acceptSubmissionEl.isSelected(0));
+		}
 
 		if(bulkAssessmentTmpDir == null) {
 			OlatRootFolderImpl bulkAssessmentDir = new OlatRootFolderImpl("/bulkassessment/", null);
@@ -270,20 +319,28 @@ public class DataStepForm extends StepFormBasicController {
 					}
 
 					targetArchive = tmpDir.createChildLeaf(uploadedFilename);
-					FileInputStream inStream = new FileInputStream(uploadedFile);
-					if(VFSManager.copyContent(inStream, targetArchive)) {
-						datas.setReturnFiles(targetArchive.getRelPath());
-						processReturnFiles(targetArchive, rows);
-					}
+					copyUploadFile(datas, uploadedFile, rows);
 				} else {
 					datas.setReturnFiles(targetArchive.getRelPath());
 					processReturnFiles(targetArchive, rows);
 				}
-			} catch (FileNotFoundException e) {
+			} catch (IOException e) {
 				logError("", e);
 			}
 		}
 	}
+	
+	private void copyUploadFile(BulkAssessmentDatas datas, File uploadedFile, List<BulkAssessmentRow> rows) throws IOException {
+		try(FileInputStream inStream = new FileInputStream(uploadedFile)) {
+			if(VFSManager.copyContent(inStream, targetArchive)) {
+				datas.setReturnFiles(targetArchive.getRelPath());
+				processReturnFiles(targetArchive, rows);
+			}
+		} catch(IOException e) {
+			logError("", e);
+			throw e;
+		}
+	}
 
 	private boolean isSame(VFSItem currentTarget, File uploadedFile) {
 		if(currentTarget instanceof LocalImpl) {
@@ -308,12 +365,11 @@ public class DataStepForm extends StepFormBasicController {
 		}
 
 		if(target.exists()) {
-			InputStream is = target.getInputStream();
 			File parentTarget = ((LocalImpl)target).getBasefile().getParentFile();
-			ZipInputStream zis = new ZipInputStream(is);
 
 			ZipEntry entry;
-			try {
+			try(InputStream is = target.getInputStream();
+					ZipInputStream zis = new ZipInputStream(is)) {
 				byte[] b = new byte[FileUtils.BSIZE];
 				while ((entry = zis.getNextEntry()) != null) {
 					if(!entry.isDirectory()) {
@@ -322,7 +378,7 @@ public class DataStepForm extends StepFormBasicController {
 						}
 
 						Path op = new File(parentTarget, entry.getName()).toPath();
-						if(!Files.isHidden(op) && !Files.isDirectory(op)) {
+						if(!Files.isHidden(op) && !op.toFile().isDirectory()) {
 							Path parentDir = op.getParent();
 							String assessedId = parentDir.getFileName().toString();
 							String filename = op.getFileName().toString();
@@ -346,9 +402,6 @@ public class DataStepForm extends StepFormBasicController {
 				}
 			} catch(Exception e) {
 				logError("", e);
-			} finally {
-				IOUtils.closeQuietly(is);
-				IOUtils.closeQuietly(zis);
 			}
 		}
 	}
diff --git a/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_de.properties b/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_de.properties
index bb6cc975939..885fbbf2c97 100644
--- a/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_de.properties
+++ b/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_de.properties
@@ -83,11 +83,18 @@ form.step2.error=Es wurden keine Bewertungsdaten eingegeben.
 form.step3.delimiter=$org.olat.course.assessment\:form.step3.delimiter
 form.step3.delimiter.comma=$org.olat.course.assessment\:form.step3.delimiter.comma
 form.step3.delimiter.tab=$org.olat.course.assessment\:form.step3.delimiter.tab
-
-
-
-
-
+form.step3.status=Status
+form.step3.status.assessed=Status "$org.olat.modules.assessment.ui\:assessment.status.done" setzen
+form.step3.status.review=Status "$org.olat.modules.assessment.ui\:assessment.status.inReview" setzen
+form.step3.status.dont.change=Nicht \u00E4ndern
+form.step3.submission=Abgabe
+form.step3.submission.accept=Abgabe akzeptieren
+form.step3.submission.dont.change=Nicht \u00E4ndern
+form.step3.submission.help=Abgabe werden nur akzeptiert (wenn Option gew?hlt ist) wenn "Bestanden" oder Punkte gesetzt werden.
+form.step3.visibility=Resultate Sichtbarkeit
+form.step3.visibility.visible=Resultate auf sichtbar setzen
+form.step3.visibility.notvisible=Resultate auf nit sichtbar setzen
+form.step3.visibility.dont.change=Sichtbarkeit nicht \u00E4ndern
 new.bulk=Neue Massenbewertung starten
 passed.false=$org.olat.course.assessment\:passed.false
 passed.true=$org.olat.course.assessment\:passed.true
diff --git a/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_en.properties b/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_en.properties
index 9312f12ddb0..6543b4028a5 100644
--- a/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_en.properties
+++ b/src/main/java/org/olat/course/assessment/bulk/_i18n/LocalStrings_en.properties
@@ -21,50 +21,6 @@ bulk.assessment.error.title=Not all evaluations could be successfully stored
 bulk.wizard.desc=With the tool "Bulk assessment" you can submit assessment data such as score, status, comments or return files for multiple course members in one step. 
 bulk.wizard.start=Select the button "$\:new.bulk" to prepare or execute a bulk assessment. More information about this process and the accepted file format can be found in the context help.
 bulk.wizard.title=Bulk assessment
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
 choose.node.desc=Select the element from this course for which the bulk assessment should be executed. 
 choose.node.title=Select course element
 chooseColumns.description=Select the columns of your assessment data should be used during the import. 
@@ -83,11 +39,18 @@ form.step2.error=No assessment data have been submitted.
 form.step3.delimiter=$org.olat.course.assessment\:form.step3.delimiter
 form.step3.delimiter.comma=$org.olat.course.assessment\:form.step3.delimiter.comma
 form.step3.delimiter.tab=$org.olat.course.assessment\:form.step3.delimiter.tab
-
-
-
-
-
+form.step3.status=Status
+form.step3.status.assessed=Set to status "$org.olat.modules.assessment.ui\:assessment.status.done"
+form.step3.status.review=Set to status "$org.olat.modules.assessment.ui\:assessment.status.inReview"
+form.step3.status.dont.change=Do not change status
+form.step3.submission=Submission
+form.step3.submission.accept=Accept submission
+form.step3.submission.dont.change=Do not change
+form.step3.submission.help=Submission are only accepted if the option is choosed and points or "passed" are set.
+form.step3.visibility=Results visibility
+form.step3.visibility.visible=Directly visible for user
+form.step3.visibility.notvisible=Not yet visible for user
+form.step3.visibility.dont.change=Do not change results visibility
 new.bulk=Start new bulk assessment
 passed.false=$org.olat.course.assessment\:passed.false
 passed.true=$org.olat.course.assessment\:passed.true
diff --git a/src/main/java/org/olat/course/assessment/manager/BulkAssessmentTask.java b/src/main/java/org/olat/course/assessment/manager/BulkAssessmentTask.java
index 52bb19b9570..96210a6de6e 100644
--- a/src/main/java/org/olat/course/assessment/manager/BulkAssessmentTask.java
+++ b/src/main/java/org/olat/course/assessment/manager/BulkAssessmentTask.java
@@ -21,7 +21,7 @@ package org.olat.course.assessment.manager;
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Date;
@@ -243,7 +243,7 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 		mail.setToId(creator);
 		mail.setFrom(WebappHelper.getMailConfig("mailReplyTo"));
 		List<Identity> modifiers = taskManager.getModifiers(task);
-		if(modifiers.size() > 0) {
+		if(!modifiers.isEmpty()) {
 			ContactList cc = new ContactList("CC");
 			cc.addAllIdentites(modifiers);
 			mail.setContactList(cc);
@@ -366,29 +366,32 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 				//esm.updateUserEfficiencyStatement(uce);
 			}
 			
+			boolean statusVisibilitySet = false;
+			
 			//update score
 			Float score = row.getScore();
-			if(hasScore && score != null){
+			if(hasScore && score != null) {
 				// score < minimum score
 				if ((min != null && score.floatValue() < min.floatValue()) || (score.floatValue() < AssessmentHelper.MIN_SCORE_SUPPORTED)) {
-					//"bulk.action.lessThanMin";
+					// "bulk.action.lessThanMin";
 				}
 				// score > maximum score
 				else if ((max != null && score.floatValue() > max.floatValue())
 						|| (score.floatValue() > AssessmentHelper.MAX_SCORE_SUPPORTED)) {
-					//"bulk.action.greaterThanMax";
+					// "bulk.action.greaterThanMax";
 				} else {
 					// score between minimum and maximum score
 					ScoreEvaluation se;
 					if (hasPassed && cut != null){
 						Boolean passed = (score.floatValue() >= cut.floatValue()) ? Boolean.TRUE	: Boolean.FALSE;
-						se = new ScoreEvaluation(score, passed);
+						se = new ScoreEvaluation(score, passed, datas.getStatus(), datas.getVisibility(), null, null, null, null);
 					} else {
-						se = new ScoreEvaluation(score, null);
+						se = new ScoreEvaluation(score, null, datas.getStatus(), datas.getVisibility(), null, null, null, null);
 					}
 					
 					// Update score,passed properties in db, and the user's efficiency statement
 					courseNode.updateUserScoreEvaluation(se, uce, coachIdentity, false, Role.auto);
+					statusVisibilitySet = true;
 				}
 			}
 			
@@ -396,14 +399,15 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 			if (hasPassed && passed != null && cut == null) { // Configuration of manual assessment --> Display passed/not passed: yes, Type of display: Manual by tutor
 				ScoreEvaluation seOld = courseNode.getUserScoreEvaluation(uce);
 				Float oldScore = seOld.getScore();
-				ScoreEvaluation se = new ScoreEvaluation(oldScore, passed);
+				ScoreEvaluation se = new ScoreEvaluation(oldScore, passed, datas.getStatus(), datas.getVisibility(), null, null, null, null);
 				// Update score,passed properties in db, and the user's efficiency statement
 				boolean incrementAttempts = false;
 				courseNode.updateUserScoreEvaluation(se, uce, coachIdentity, incrementAttempts, Role.auto);
+				statusVisibilitySet = true;
 			}
 			
 			boolean identityHasReturnFile = false;
-			if(hasReturnFiles && row.getReturnFiles() != null && row.getReturnFiles().size() > 0) {
+			if(hasReturnFiles && row.getReturnFiles() != null && !row.getReturnFiles().isEmpty()) {
 				String assessedId = row.getAssessedId();
 				File assessedFolder = new File(unzipped, assessedId);
 				identityHasReturnFile = assessedFolder.exists();
@@ -413,17 +417,29 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 			}
 			
 			if(courseNode instanceof GTACourseNode) {
+				boolean acceptSubmission = datas.getAcceptSubmission() != null && datas.getAcceptSubmission().booleanValue();
+
 				//push the state further
 				GTACourseNode gtaNode = (GTACourseNode)courseNode;
 				if((hasScore && score != null) || (hasPassed && passed != null)) {
 					//pushed to graded
-					updateTasksState(gtaNode, uce, TaskProcess.grading);
+					updateTasksState(gtaNode, uce, TaskProcess.grading, acceptSubmission);
 				} else if(hasReturnFiles) {
 					//push to revised
-					updateTasksState(gtaNode, uce, TaskProcess.correction);
+					updateTasksState(gtaNode, uce, TaskProcess.correction, acceptSubmission);
 				}
 			}
 			
+			if(!statusVisibilitySet && (datas.getStatus() != null || datas.getVisibility() != null)) {
+				ScoreEvaluation seOld = courseNode.getUserScoreEvaluation(uce);
+				ScoreEvaluation se = new ScoreEvaluation(seOld.getScore(), seOld.getPassed(),
+						datas.getStatus(), datas.getVisibility(), seOld.getFullyAssessed(),
+						seOld.getCurrentRunCompletion(), seOld.getCurrentRunStatus(), seOld.getAssessmentID());
+				// Update score,passed properties in db, and the user's efficiency statement
+				boolean incrementAttempts = false;
+				courseNode.updateUserScoreEvaluation(se, uce, coachIdentity, incrementAttempts, Role.auto);
+			}
+			
 			if(count++ % 5 == 0) {
 				dbInstance.commitAndCloseSession();
 			} else {
@@ -432,7 +448,7 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 		}
 	}
 	
-	private void updateTasksState(GTACourseNode courseNode, UserCourseEnvironment uce, TaskProcess status) {
+	private void updateTasksState(GTACourseNode courseNode, UserCourseEnvironment uce, TaskProcess status, boolean acceptSubmission) {
 		final GTAManager gtaManager = CoreSpringFactory.getImpl(GTAManager.class);
 		Identity identity = uce.getIdentityEnvironment().getIdentity();
 		RepositoryEntry entry = uce.getCourseEnvironment().getCourseGroupManager().getCourseEntry();
@@ -445,14 +461,26 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 		} else {
 			gtaTask = gtaManager.getTask(identity, taskList);
 			if(gtaTask == null) {
-				gtaManager.createTask(null, taskList, status, null, identity, courseNode);
+				gtaTask = gtaManager.createTask(null, taskList, status, null, identity, courseNode);
 			}
 		}
 		
-		gtaManager.nextStep(status, courseNode);
+		if(gtaTask == null) {
+			log.error("GTA Task is null by bulk assessment for: " + identity + " in entry:" + entry + " " + courseNode.getIdent());
+		} else if(status == TaskProcess.correction) {
+			int iteration = gtaTask.getRevisionLoop() <= 0 ? 1 : gtaTask.getRevisionLoop() + 1;
+			gtaManager.updateTask(gtaTask, status, iteration, courseNode, Role.auto);
+		} else if(status == TaskProcess.grading && acceptSubmission) {
+			if(gtaTask.getTaskStatus() == TaskProcess.review
+					|| gtaTask.getTaskStatus() == TaskProcess.correction
+					|| gtaTask.getTaskStatus() == TaskProcess.revision) {
+				gtaTask = gtaManager.reviewedTask(gtaTask, courseNode, Role.auto);
+			}
+			TaskProcess nextStep = gtaManager.nextStep(status, courseNode);
+			gtaManager.updateTask(gtaTask, nextStep, courseNode, Role.auto);
+		}
 	}
 	
-	
 	private void processReturnFile(AssessableCourseNode courseNode, BulkAssessmentRow row, UserCourseEnvironment uce, File assessedFolder) {
 		String assessedId = row.getAssessedId();
 		Identity identity = uce.getIdentityEnvironment().getIdentity();
@@ -468,10 +496,9 @@ public class BulkAssessmentTask implements LongRunnable, TaskAwareRunnable, Sequ
 
 				VFSLeaf returnLeaf = returnBox.createChildLeaf(returnFilename);
 				if(returnFile.exists()) {
-					try {
-						InputStream inStream = new FileInputStream(returnFile);
+					try(InputStream inStream = new FileInputStream(returnFile)) {
 						VFSManager.copyContent(inStream, returnLeaf);
-					} catch (FileNotFoundException e) {
+					} catch (IOException e) {
 						log.error("Cannot copy return file " + returnFilename + " from " + assessedId, e);
 					}
 				}
diff --git a/src/main/java/org/olat/course/assessment/model/BulkAssessmentDatas.java b/src/main/java/org/olat/course/assessment/model/BulkAssessmentDatas.java
index d0225276893..62f95ab1b78 100644
--- a/src/main/java/org/olat/course/assessment/model/BulkAssessmentDatas.java
+++ b/src/main/java/org/olat/course/assessment/model/BulkAssessmentDatas.java
@@ -22,6 +22,8 @@ package org.olat.course.assessment.model;
 import java.io.Serializable;
 import java.util.List;
 
+import org.olat.modules.assessment.model.AssessmentEntryStatus;
+
 /**
  * 
  * Initial date: 20.11.2013<br>
@@ -31,9 +33,13 @@ import java.util.List;
 public class BulkAssessmentDatas implements Serializable {
 
 	private static final long serialVersionUID = 8109609348537626355L;
+	
 	private List<BulkAssessmentRow> rows;
 	private String returnFiles;
 	private String dataBackupFile;
+	private Boolean visibility;
+	private Boolean acceptSubmission;
+	private AssessmentEntryStatus status;
 	private BulkAssessmentColumnSettings columnsSettings;
 
 	public BulkAssessmentColumnSettings getColumnsSettings() {
@@ -71,4 +77,28 @@ public class BulkAssessmentDatas implements Serializable {
 	public void setDataBackupFile(String dataBackupFile) {
 		this.dataBackupFile = dataBackupFile;
 	}
+
+	public Boolean getVisibility() {
+		return visibility;
+	}
+
+	public void setVisibility(Boolean visibility) {
+		this.visibility = visibility;
+	}
+
+	public AssessmentEntryStatus getStatus() {
+		return status;
+	}
+
+	public void setStatus(AssessmentEntryStatus status) {
+		this.status = status;
+	}
+
+	public Boolean getAcceptSubmission() {
+		return acceptSubmission;
+	}
+
+	public void setAcceptSubmission(Boolean acceptSubmission) {
+		this.acceptSubmission = acceptSubmission;
+	}
 }
diff --git a/src/main/java/org/olat/course/nodes/gta/GTAManager.java b/src/main/java/org/olat/course/nodes/gta/GTAManager.java
index ef12ab7cf73..e9cc7a85b34 100644
--- a/src/main/java/org/olat/course/nodes/gta/GTAManager.java
+++ b/src/main/java/org/olat/course/nodes/gta/GTAManager.java
@@ -337,7 +337,7 @@ public interface GTAManager {
 	 * @param cNode
 	 * @return
 	 */
-	public Task reviewedTask(Task task, GTACourseNode cNode);
+	public Task reviewedTask(Task task, GTACourseNode cNode, Role by);
 	
 	public Task updateTask(Task task, TaskProcess newStatus, GTACourseNode cNode, Role by);
 	
diff --git a/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java b/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java
index 6bedc4ffa32..927e4908101 100644
--- a/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java
+++ b/src/main/java/org/olat/course/nodes/gta/manager/GTAManagerImpl.java
@@ -1538,11 +1538,11 @@ public class GTAManagerImpl implements GTAManager {
 	}
 	
 	@Override
-	public Task reviewedTask(Task task, GTACourseNode cNode) {
+	public Task reviewedTask(Task task, GTACourseNode cNode, Role by) {
 		TaskProcess solution = nextStep(TaskProcess.correction, cNode);
 		TaskImpl taskImpl = (TaskImpl)task;
 		taskImpl.setAcceptationDate(new Date());
-		return updateTask(taskImpl, solution, cNode, Role.coach);
+		return updateTask(taskImpl, solution, cNode, by);
 	}
 
 	@Override
diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachController.java
index ffdcdd05e52..819fa87e385 100644
--- a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachController.java
+++ b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachController.java
@@ -51,6 +51,7 @@ import org.olat.course.nodes.GTACourseNode;
 import org.olat.course.nodes.gta.GTAType;
 import org.olat.course.nodes.gta.Task;
 import org.olat.course.nodes.gta.TaskHelper;
+import org.olat.course.nodes.gta.TaskList;
 import org.olat.course.nodes.gta.TaskHelper.FilesLocked;
 import org.olat.course.nodes.gta.TaskProcess;
 import org.olat.course.nodes.gta.model.DueDate;
@@ -560,13 +561,9 @@ public class GTACoachController extends GTAAbstractController implements Assessm
 
 	@Override
 	protected void event(UserRequest ureq, Controller source, Event event) {
-		if(revisionDocumentsCtrl == source) {
-			cleanUpProcess();
-			process(ureq);
-		} else if(participantGradingCtrl == source) {
-			cleanUpProcess();
-			process(ureq);
-		} else if(groupGradingCtrl == source) {
+		if(revisionDocumentsCtrl == source
+				|| participantGradingCtrl == source
+				|| groupGradingCtrl == source) {
 			cleanUpProcess();
 			process(ureq);
 		} else if(submitCorrectionsCtrl == source) {
@@ -664,7 +661,13 @@ public class GTACoachController extends GTAAbstractController implements Assessm
 	
 	private void doReviewedDocument(UserRequest ureq, Task task) {
 		//go to solution, grading or graded
-		gtaManager.reviewedTask(task, gtaNode);
+		if(task == null) {
+			TaskProcess firstStep = gtaManager.firstStep(gtaNode);
+			TaskList reloadedTaskList = gtaManager.getTaskList(courseEnv.getCourseGroupManager().getCourseEntry(), gtaNode);
+			task = gtaManager.createAndPersistTask(null, reloadedTaskList, firstStep, assessedGroup, assessedIdentity, gtaNode);
+		}
+		
+		gtaManager.reviewedTask(task, gtaNode, Role.coach);
 		showInfo("coach.documents.successfully.reviewed");
 		gtaManager.log("Review", "documents reviewed", task, getIdentity(), assessedIdentity, assessedGroup, courseEnv, gtaNode, Role.coach);
 		
@@ -818,7 +821,7 @@ public class GTACoachController extends GTAAbstractController implements Assessm
 			contactList.add(assessedIdentity);			
 		}
 		// open dialog with mail form
-		if (contactList != null && contactList.getEmailsAsStrings().size() > 0) {
+		if (contactList != null && !contactList.getEmailsAsStrings().isEmpty()) {
 			removeAsListenerAndDispose(emailController);
 			
 			ContactMessage cmsg = new ContactMessage(ureq.getIdentity());
diff --git a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachRevisionAndCorrectionsController.java b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachRevisionAndCorrectionsController.java
index 01f625edd09..a70ad880f48 100644
--- a/src/main/java/org/olat/course/nodes/gta/ui/GTACoachRevisionAndCorrectionsController.java
+++ b/src/main/java/org/olat/course/nodes/gta/ui/GTACoachRevisionAndCorrectionsController.java
@@ -411,7 +411,7 @@ public class GTACoachRevisionAndCorrectionsController extends BasicController im
 	}
 	
 	private void doCloseRevisionProcess() {
-		assignedTask = gtaManager.reviewedTask(assignedTask, gtaNode);
+		assignedTask = gtaManager.reviewedTask(assignedTask, gtaNode, Role.coach);
 		gtaManager.log("Revision", "close revision", assignedTask,
 				getIdentity(), assessedIdentity, assessedGroup, courseEnv, gtaNode, Role.coach);
 	}
-- 
GitLab