diff --git a/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties b/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
index 79063565e1b6..6e60ae0507ce 100644
--- a/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
+++ b/config/configuration/bundles/src/bundle/org/sakaiproject/config/bundle/default.sakai.properties
@@ -1958,6 +1958,10 @@
# DEFAULT: false
# gradebookng.allowColumnResizing=true
+# SAK-50770
+# DEFAULT: false
+# gradebookng.export.enabelOsirisExport=true
+
# SAK-46075: max upload file size, defined in megabytes
# DEFAULT: 2
# gradebook.import.maxSize=3
diff --git a/gradebookng/bundle/src/main/bundle/gradebookng.properties b/gradebookng/bundle/src/main/bundle/gradebookng.properties
index ccbe40d53c11..78ef9c7e93cd 100644
--- a/gradebookng/bundle/src/main/bundle/gradebookng.properties
+++ b/gradebookng/bundle/src/main/bundle/gradebookng.properties
@@ -275,6 +275,10 @@ importExport.template.button.fullGradebook = Export Gradebook
importExport.template.button.customGradebook = Download Custom Export
importExport.template.button.advancedOptions = Custom Export
+importExport.export.osiris.heading = Export to Osiris
+importExport.export.osiris.description = Export final grades in a format compatible with the Osiris SIS platform
+importExport.template.button.export.osiris = Osiris export
+
importExport.selection.heading = Gradebook Item Import Selection
importExport.selection.description = The system has analyzed the contents of your file upload and has identified new/updated information where applicable. Please select from the desired items below.
importExport.selection.note = Note: Selecting "Update" items will override existing values for that item.
diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java
index 48ca8f821166..2b6c13608dfa 100644
--- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java
+++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/business/util/EventHelper.java
@@ -141,10 +141,20 @@ public static void postStudentViewEvent(Gradebook gradebook, String studentUid)
public static void postExportEvent(Gradebook gradebook, boolean isCustomExport) {
+ postExportEvent(gradebook, isCustomExport ? "custom" : "full");
+ }
+
+
+ public static void postOsirisExportEvent(Gradebook gradebook) {
+ postExportEvent(gradebook, "osiris");
+ }
+
+
+ private static void postExportEvent(Gradebook gradebook, String exportType) {
String[] bits = new String[] {
EVENT_REF_PREFIX,
String.valueOf(gradebook.getId()),
- isCustomExport ? "custom" : "full"
+ exportType
};
postEvent(createEvent(GbEvent.EXPORT, String.join("/", bits), false));
diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BasePanel.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BasePanel.java
index d0953bbb7a92..3024bc6d100b 100644
--- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BasePanel.java
+++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/BasePanel.java
@@ -68,6 +68,9 @@ public abstract class BasePanel extends Panel {
protected static final String SAK_PROP_ALLOW_COMPARE_GRADES = "gradebookng.allowStudentsToCompareGradesWithClassmates";
protected static final Boolean SAK_PROP_ALLOW_COMPARE_GRADES_DEFAULT = Boolean.FALSE;
+ protected static final String SAK_PROP_ENABLE_OSIRIS_EXPORT = "gradebookng.export.enabelOsirisExport";
+ protected static final Boolean SAK_PROP_ENABLE_OSIRIS_EXPORT_DEFAULT = Boolean.FALSE;
+
public BasePanel(final String id) {
super(id);
}
diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.html b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.html
index f8a216879eaa..2662b3aea91d 100644
--- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.html
+++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.html
@@ -154,6 +154,26 @@
+
+
+
+
+
+
diff --git a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.java b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.java
index 444608fddb14..ac3d3e7139b1 100644
--- a/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.java
+++ b/gradebookng/tool/src/java/org/sakaiproject/gradebookng/tool/panels/importExport/ExportPanel.java
@@ -19,20 +19,28 @@
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.math.RoundingMode;
import java.nio.charset.StandardCharsets;
+import java.text.DecimalFormatSymbols;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.math.NumberUtils;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormComponentUpdatingBehavior;
import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox;
+import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.link.DownloadLink;
@@ -42,6 +50,7 @@
import org.sakaiproject.gradebookng.business.model.GbGradeInfo;
import org.sakaiproject.gradebookng.business.model.GbGroup;
import org.sakaiproject.gradebookng.business.model.GbStudentGradeInfo;
+import org.sakaiproject.gradebookng.business.model.GbUser;
import org.sakaiproject.gradebookng.business.util.EventHelper;
import org.sakaiproject.gradebookng.business.util.FormatHelper;
import org.sakaiproject.gradebookng.tool.model.GradebookUiSettings;
@@ -54,6 +63,9 @@
import org.sakaiproject.util.Validator;
import org.sakaiproject.util.api.FormattedText;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
public class ExportPanel extends BasePanel {
private static final long serialVersionUID = 1L;
@@ -282,6 +294,24 @@ protected File load() {
this.customDownloadLink = buildCustomDownloadLink();
add(this.customDownloadLink);
+
+ // Add Osiris export components if enabled
+ boolean osirisExportEnabled =this.serverConfigService.getBoolean(SAK_PROP_ENABLE_OSIRIS_EXPORT,
+ SAK_PROP_ENABLE_OSIRIS_EXPORT_DEFAULT);
+
+ WebMarkupContainer osirisExportContainer = new WebMarkupContainer("osirisExportContainer");
+ osirisExportContainer.setVisible(osirisExportEnabled);
+ add(osirisExportContainer);
+ osirisExportContainer.add(new DownloadLink("downloadOsirisExport", new LoadableDetachableModel() {
+ private static final long serialVersionUID = 2L;
+
+ @Override
+ protected File load() {
+ return buildOsirisExportFile();
+ }
+
+ }, buildOsirisExportFileName()).setCacheDuration(Duration.ZERO).setDeleteAfterDownload(true));
+
}
private Component buildCustomDownloadLink() {
@@ -535,4 +565,63 @@ private String buildFileName(final boolean customDownload) {
return String.format("%s.%s", cleanFilename, extension);
}
+
+ private File buildOsirisExportFile() {
+ List userIds = this.businessService.getGradeableUsers();
+
+ Map userEids = this.businessService.getGbUsers(userIds).stream()
+ .collect(Collectors.toMap(GbUser::getUserUuid, GbUser::getDisplayId));
+
+ Map courseGrades = this.businessService.getCourseGrades(userIds);
+
+ try {
+ File tempFile = File.createTempFile("gradebookExport", ".txt");
+
+ try (PrintWriter writer = new PrintWriter(new FileWriter(tempFile))) {
+ for (String userId : userIds) {
+ String userEid = userEids.get(userId);
+ if (StringUtils.isEmpty(userEid)) {
+ log.debug("Skipping user {} from export, beacuse eid is empty", userId);
+ continue;
+ }
+
+ CourseGradeTransferBean courseGrade = courseGrades.get(userId);
+ if (courseGrade == null || StringUtils.isEmpty(courseGrade.getCalculatedGrade())) {
+ log.debug("Skipping user {} from export, beacuse display grade is empty", userId);
+ continue;
+ }
+
+ DecimalFormatSymbols.getInstance(Locale.US);
+ String grade = NumberUtils.toScaledBigDecimal(courseGrade.getCalculatedGrade(), 0, RoundingMode.HALF_UP).toString();
+
+ writer.println(StringUtils.trim(userEid) + " " + grade);
+ }
+ }
+
+ EventHelper.postOsirisExportEvent(getGradebook());
+
+ return tempFile;
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to create Osiris export", e);
+ }
+ }
+
+ private String buildOsirisExportFileName() {
+ final String prefix = getString("importExport.download.filenameprefix");
+
+ final List fileNameComponents = new ArrayList<>();
+ fileNameComponents.add(prefix);
+
+ // Add gradebook name/site id to filename
+ final String gradebookName = this.businessService.getGradebook().getName();
+ if (StringUtils.isNotBlank(gradebookName)) {
+ fileNameComponents.add(StringUtils.replace(gradebookName, " ", "_"));
+ }
+
+ fileNameComponents.add("osiris");
+
+ final String cleanFilename = Validator.cleanFilename(StringUtils.join(fileNameComponents, "-"));
+
+ return cleanFilename + ".txt";
+ }
}