Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SAK-50733 Date Manager CSV export/import and Excel date compatibility #13079

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,10 @@
package org.sakaiproject.datemanager.impl;

import java.time.Instant;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
Expand Down Expand Up @@ -691,9 +693,9 @@ public void updateAssessments(DateManagerValidation assessmentsValidation) throw
*/
@Override
public JSONArray getGradebookItemsForContext(String siteId) {
JSONArray jsonAssignments = new JSONArray();
JSONArray jsonGradebook = new JSONArray();
if(!gradingService.currentUserHasEditPerm(getCurrentSiteId())) {
return jsonAssignments;
return jsonGradebook;
}
Collection<org.sakaiproject.grading.api.Assignment> gbitems = gradingService.getAssignments(siteId);
String url = getUrlForTool(DateManagerConstants.COMMON_ID_GRADEBOOK);
Expand All @@ -703,14 +705,14 @@ public JSONArray getGradebookItemsForContext(String siteId) {
JSONObject gobj = new JSONObject();
gobj.put(DateManagerConstants.JSON_ID_PARAM_NAME, gbitem.getId());
gobj.put(DateManagerConstants.JSON_TITLE_PARAM_NAME, gbitem.getName());
gobj.put(DateManagerConstants.JSON_DUEDATE_PARAM_NAME, gbitem.getDueDate());
gobj.put(DateManagerConstants.JSON_DUEDATE_PARAM_NAME, formatToUserDateFormat(gbitem.getDueDate()));
gobj.put(DateManagerConstants.JSON_TOOLTITLE_PARAM_NAME, toolTitle);
gobj.put(DateManagerConstants.JSON_URL_PARAM_NAME, url);
gobj.put(DateManagerConstants.JSON_EXTRAINFO_PARAM_NAME, "false");
jsonAssignments.add(gobj);
jsonGradebook.add(gobj);
}
}
return orderJSONArrayByTitle(jsonAssignments);
return orderJSONArrayByTitle(jsonGradebook);
}

/**
Expand Down Expand Up @@ -746,6 +748,18 @@ public DateManagerValidation validateGradebookItems(String siteId, JSONArray gra
String dueDateRaw = (String) jsonItem.get(DateManagerConstants.JSON_DUEDATE_PARAM_NAME);
Instant dueDate = null;
if (StringUtils.isNotBlank(dueDateRaw)) {
dueDateRaw = dueDateRaw.replaceAll("\"", "").replace("/", "-");
try {
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("[M/d/yyyy][MM/dd/yyyy][dd-MM-yyyy][d-M-yyyy][yyyy-MM-dd][yyyy-M-d]");
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(dueDateRaw, inputFormatter);
dueDateRaw = date.format(outputFormatter);
} catch (DateTimeParseException e) {
log.error("Error parsing date: " + dueDateRaw, e);
}
if (!dueDateRaw.contains("T")) {
dueDateRaw += "T00:00:00";
}
dueDate = userTimeService.parseISODateInUserTimezone(dueDateRaw).toInstant();
}

Expand Down Expand Up @@ -1811,11 +1825,9 @@ public DateManagerValidation validateTool(String toolId, int idx, String[][] col

if (StringUtils.isBlank((String)resourcesJsonObject.get("open_date"))) {
resourcesJsonObject.remove("open_date");
resourcesJsonObject.put("open_date", ZonedDateTime.now().toString());
}
if (StringUtils.isBlank((String)resourcesJsonObject.get("due_date"))) {
resourcesJsonObject.remove("due_date");
resourcesJsonObject.put("due_date", ZonedDateTime.now().toString());
}

JSONArray resourcesJsonArray = new JSONArray();
Expand Down Expand Up @@ -1860,11 +1872,9 @@ public DateManagerValidation validateTool(String toolId, int idx, String[][] col

if (StringUtils.isBlank((String) announcementJsonObject.get("open_date"))) {
announcementJsonObject.remove("open_date");
announcementJsonObject.put("open_date", ZonedDateTime.now().toString());
}
if (StringUtils.isBlank((String) announcementJsonObject.get("due_date"))) {
announcementJsonObject.remove("due_date");
announcementJsonObject.put("due_date", ZonedDateTime.now().toString());
}
JSONArray announcementJsonArray = new JSONArray();
announcementJsonArray.add(announcementJsonObject);
Expand Down Expand Up @@ -1958,7 +1968,23 @@ public boolean isChanged(String toolId, String[] columns) {
}
} else if (DateManagerConstants.COMMON_ID_GRADEBOOK.equals(toolId.replaceAll("\"", ""))) {
org.sakaiproject.grading.api.Assignment gbitem = gradingService.getAssignment(getCurrentSiteId(), Long.parseLong(id));
changed = this.compareDates(gbitem.getDueDate(), columns[2]);
if (columns[2] != null && columns[2].matches(".*\\d.*")) {
columns[2] = columns[2].replaceAll("\"", "").replace("/", "-");
try {
DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern("[M/d/yyyy][MM/dd/yyyy][dd-MM-yyyy][d-M-yyyy][yyyy-MM-dd]");
DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
LocalDate date = LocalDate.parse(columns[2], inputFormatter);
columns[2] = date.format(outputFormatter);
} catch (DateTimeParseException e) {
log.error("Error parsing date: " + columns[2], e);
}
if (!columns[2].contains("T")) {
columns[2] += "T00:00:00";
}
changed = this.compareDates(gbitem.getDueDate(), columns[2]);
} else if (gbitem.getDueDate() != null) {
changed = true; // remove due_date
}
} else if (DateManagerConstants.COMMON_ID_SIGNUP.equals(toolId.replaceAll("\"", ""))) {
SignupMeeting meeting = signupService.loadSignupMeeting(Long.parseLong(id), getCurrentUserId(), siteId);
changed = this.compareDates(meeting.getStartTime(), columns[2])
Expand Down Expand Up @@ -2010,13 +2036,13 @@ public boolean isChanged(String toolId, String[] columns) {
if (announcement.getId().equals(id)) {
if (releaseDateExist) {
changed = changed || this.compareDates(Date.from(announcement.getProperties().getInstantProperty(AnnouncementService.RELEASE_DATE)), columns[2]);
} else if (columns[2] != null && columns[2].matches(".*\\d.*")) {
changed = true; // new release_date
}
if (retractDateExist) {
String retractDate = columns[2];
if (columns.length > 3) {
retractDate = columns[3];
}
changed = changed || this.compareDates(Date.from(announcement.getProperties().getInstantProperty(AnnouncementService.RETRACT_DATE)), retractDate);
changed = changed || this.compareDates(Date.from(announcement.getProperties().getInstantProperty(AnnouncementService.RETRACT_DATE)), columns[3]);
} else if (columns[3] != null && columns[3].matches(".*\\d.*")) {
changed = true; // new retract_date
}
}
i++;
Expand All @@ -2029,8 +2055,14 @@ public boolean isChanged(String toolId, String[] columns) {
int i = 0;
while (i < jsonLessons.size() && !changed) {
JSONObject lesson = (JSONObject) jsonLessons.get(i);
if (Long.toString((Long)lesson.get("id")).equals(id)) {
changed = this.compareDates(this.stringToDate((String) lesson.get("open_date")), columns[2]);
if (Long.toString((Long)lesson.get("id")).equals(id) && columns[2] != null && columns[2].matches(".*\\d.*")) {
if (lesson.get("open_date") != null) {
changed = this.compareDates(this.stringToDate((String) lesson.get("open_date")), columns[2]);
} else {
changed = true; // new open_date
}
} else if (Long.toString((Long)lesson.get("id")).equals(id) && !columns[2].matches(".*\\d.*") && lesson.get("open_date") != null) {
changed = true; // remove open_date
}
i++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.ArrayList;
import java.util.Locale;
import java.util.List;
import java.util.Arrays;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
Expand Down Expand Up @@ -76,9 +77,11 @@ public class MainController {

private static final String BOM = "\uFEFF";

private String[] columnsCsvStrings = {"id", "title", "open.date", "available.date", "start.date", "show.from.date",
"hide.until", "due.date", "end.date", "accept.until", "show.until", "close.date", "feedback.start.date",
"feedback.end.date", "signup.begins.date", "signup.deadline.date", "extra.info"};
private String[] columnsCsvStrings = {"id", "title", "open.date.required", "open.date.optional", "available.date", "available.date.required",
"start.date", "start.date.required", "start.date.optional", "show.from.date.optional","hide.until.optional", "due.date",
"due.date.required", "due.date.optional", "end.date", "end.date.required", "end.date.optional", "assessments.accept.until",
"accept.until.required", "show.until.optional", "close.date.optional", "feedback.start.date","feedback.end.date", "signup.begins.date",
"signup.deadline.date", "extra.info"};

private String[][] columnsNames = {{"id", "title", "open_date", "due_date", "accept_until"},
{"id", "title", "open_date", "due_date", "accept_until", "feedback_start", "feedback_end"},
Expand Down Expand Up @@ -296,63 +299,63 @@ public ResponseEntity<byte[]> exportCsv(HttpServletRequest req) {
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_ASSIGNMENTS)) {
JSONArray assignmentsJson = dateManagerService.getAssignmentsForContext(siteId);
if (assignmentsJson.size() > 0) {
int[] columnsIndex = {0, 1, 2, 7, 9};
int[] columnsIndex = {0, 1, 2, 12, 18};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_ASSIGNMENTS, columnsIndex, assignmentsJson, columnsNames[0]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_ASSESSMENTS)) {
JSONArray assessmentsJson = dateManagerService.getAssessmentsForContext(siteId);
if (assessmentsJson.size() > 0) {
int[] columnsIndex = {0, 1, 3, 7, 9, 12, 13};
int[] columnsIndex = {0, 1, 5, 11, 17, 21, 22};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_ASSESSMENTS, columnsIndex, assessmentsJson, columnsNames[1]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_GRADEBOOK)) {
JSONArray gradebookItemsJson = dateManagerService.getGradebookItemsForContext(siteId);
if (gradebookItemsJson.size() > 0) {
int[] columnsIndex = {0, 1, 7};
int[] columnsIndex = {0, 1, 13};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_GRADEBOOK, columnsIndex, gradebookItemsJson, columnsNames[2]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_SIGNUP)) {
JSONArray signupMeetingsJson = dateManagerService.getSignupMeetingsForContext(siteId);
if (signupMeetingsJson.size() > 0) {
int[] columnsIndex = {0, 1, 4, 8, 14, 15};
int[] columnsIndex = {0, 1, 6, 14, 23, 24};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_SIGNUP, columnsIndex, signupMeetingsJson, columnsNames[3]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_RESOURCES)) {
JSONArray resourcesJson = dateManagerService.getResourcesForContext(siteId);
if (resourcesJson.size() > 0) {
int[] columnsIndex = {0, 1, 5, 10, 16};
int[] columnsIndex = {0, 1, 9, 19, 25};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_RESOURCES, columnsIndex, resourcesJson, columnsNames[5]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_CALENDAR)) {
JSONArray calendarJson = dateManagerService.getCalendarEventsForContext(siteId);
if (calendarJson.size() > 0) {
int[] columnsIndex = {0, 1, 4, 8};
int[] columnsIndex = {0, 1, 7, 15};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_CALENDAR, columnsIndex, calendarJson, columnsNames[4]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_FORUMS)) {
JSONArray forumsJson = dateManagerService.getForumsForContext(siteId);
if (forumsJson.size() > 0) {
int[] columnsIndex = {0, 1, 2, 11, 16};
int[] columnsIndex = {0, 1, 3, 20, 25};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_FORUMS, columnsIndex, forumsJson, columnsNames[5]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_ANNOUNCEMENTS)) {
JSONArray announcementsJson = dateManagerService.getAnnouncementsForContext(siteId);
if (announcementsJson.size() > 0) {
int[] columnsIndex = {0, 1, 4, 8};
int[] columnsIndex = {0, 1, 8, 16};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_ANNOUNCEMENTS, columnsIndex, announcementsJson, columnsNames[4]);
}
}
if (dateManagerService.currentSiteContainsTool(DateManagerConstants.COMMON_ID_LESSONS)) {
JSONArray lessonsJson = dateManagerService.getLessonsForContext(siteId);
if (lessonsJson.size() > 0) {
int[] columnsIndex = {0, 1, 6};
int[] columnsIndex = {0, 1, 10};
this.createCsvSection(gradesBuffer, DateManagerConstants.COMMON_ID_LESSONS, columnsIndex, lessonsJson, columnsNames[6]);
}
}
Expand Down Expand Up @@ -421,6 +424,16 @@ public void createCsvSection(CSVWriter gradesBuffer, String toolId, int[] column
toolColumns[j] = toolInfoString != null? toolInfoString : "";
} else {
String toolInfoString = ((String) toolInfoObject);
if (columnsNames[j].equals("title")) {
toolInfoString = toolInfoString.replaceAll("[;,\"]", "_");
}
String extraInfo = (String) ((JSONObject) toolJson.get(i)).get(DateManagerConstants.JSON_EXTRAINFO_PARAM_NAME);
if (columnsNames[j].equals("title") && extraInfo != null && extraInfo.contains(rb.getString("itemtype.draft"))) {
toolInfoString += " (" + rb.getString("itemtype.draft") + ")";
}
if (DateManagerConstants.COMMON_ID_GRADEBOOK.equals(toolId) && !columnsNames[j].equals("title")) {
toolInfoString = toolInfoString.split("T")[0];
}
toolColumns[j] = toolInfoString != null? toolInfoString : "";
}
}
Expand Down Expand Up @@ -475,21 +488,52 @@ public String importDates(Model model, HttpServletRequest request, HttpServletRe
int idx = 0;

String[] nextLine;
boolean nextLineIsHeader = false;
while ((nextLine = reader.readNext()) != null) {
String csvLine = nextLine[0];
if (!csvLine.contains("\"")) {
if (csvLine.matches(";+") || csvLine.matches(",+")) {
nextLineIsHeader = true;
}
if (nextLineIsHeader) {
csvLine = csvLine.replaceAll(";", "");
csvLine = csvLine.replaceAll(",", "");
}
else {
csvLine = csvLine.replaceAll(";", "\";\"");
csvLine = csvLine.replaceAll(",", "\";\"");
}
}
if (csvLine.indexOf(";") == -1) {
if (csvLine.length() > 0) {
String toolId = csvLine.indexOf("(") != -1? csvLine.substring(0, csvLine.indexOf("(")).replaceAll("\"", "") : csvLine;
currentToolId = toolId;
isHeader = true;
isAnotherTool = false;
nextLineIsHeader = false;
} else {
isAnotherTool = true;
}
} else {
if (!csvLine.contains("\"")) {
if (csvLine.contains(";")) {
csvLine = csvLine.replaceAll(";", "\";\"");
} else {
csvLine = csvLine.replaceAll(",", "\",\"");
}
}
String[] toolColumnsAux = csvLine.split(";\"");
int toolColumnsAuxSize = (toolHeader.size() > 0? ((String[]) toolHeader.get(0)).length : toolColumnsAux.length - 1);
String[] toolColumns = new String[toolColumnsAuxSize];

if (toolColumnsAux.length < toolColumnsAuxSize + 1) {
int toolColumnsAuxPreviusSize = toolColumnsAux.length;
toolColumnsAux = Arrays.copyOf(toolColumnsAux, toolColumnsAuxSize + 1);
for (int i = toolColumnsAuxPreviusSize; i < toolColumnsAuxSize + 1; i++) {
toolColumnsAux[i] = "";
}
}

// We ignore here the first column because it is the id, and it will not shown
boolean isChanged = true;
if (!isHeader) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,30 @@ button.start.again = Start Again
column.id=Id
column.title = Title
column.open.date = Open Date
column.open.date.required = Open Date (Required)
column.open.date.optional = Open Date (Optional)
column.available.date = Available Date
column.available.date.required = Available Date (Required)
column.start.date = Start Date
column.start.date.required = Start Date (Required)
column.start.date.optional = Start Date (Optional)
column.show.from.date = Show From Date
column.show.from.date.optional = Show From Date (Optional)
column.hide.until = Hide Until Date
column.hide.until.optional = Hide Until Date (Optional)
column.due.date = Due Date
column.due.date.required = Due Date (Required)
column.due.date.optional = Due Date (Optional)
column.end.date = End Date
column.end.date.required = End Date (Required)
column.end.date.optional = End Date (Optional)
column.accept.until = Accept Until Date
column.accept.until.required = Accept Until Date (Required)
column.assessments.accept.until = Late Submission Deadline
column.show.until = Show Until Date
column.show.until.optional = Show Until Date (Optional)
column.close.date = Close Date
column.close.date.optional = Close Date (Optional)
column.feedback.start.date = Show Feedback Date
column.feedback.end.date = Hide Feedback Date
column.signup.begins.date = Sign-up Begin Date
Expand All @@ -33,6 +47,12 @@ modal.confirm.instruction = You are about to modify the following course dates:
page.instructions = Edit dates in batch

success.message = The dates were updated successfully.
success.export = The dates were exported successfully. Please note the following considerations:
success.export.1 = The fields marked as required cannot be left blank.
success.export.2 = The fields marked as optional can be left blank.
success.export.3 = The fields not marked as required or optional can only be modified if they already have a value or left blank if they do not.
success.export.4 = Only modify the date fields. Do not modify the rest of the fields.
success.export.5 = Do not change the date format.

dateshifter.label.before=Shift dates by
dateshifter.label.after=day(s). Remember to click Save Changes at the bottom of the page to save your adjustments.
Expand All @@ -54,3 +74,5 @@ page.import.error.no.csv.file=Has not selected any valid csv file

template.error={0} ({1})
warn.message=Please fix the following errors before submitting the csv again

itemtype.draft = Draft
Loading
Loading