/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.course.nodes.cl.ui;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import org.apache.commons.lang.StringEscapeUtils;
import org.olat.core.commons.persistence.SortKey;
import org.olat.core.gui.components.form.flexible.elements.FlexiTableFilter;
import org.olat.core.gui.components.form.flexible.elements.MultipleSelectionElement;
import org.olat.core.gui.components.form.flexible.impl.elements.table.DefaultFlexiTableDataModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.ExportableFlexiTableDataModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FilterableFlexiTableModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableColumnModel;
import org.olat.core.gui.components.form.flexible.impl.elements.table.FlexiTableComponent;
import org.olat.core.gui.components.form.flexible.impl.elements.table.SortableFlexiTableDataModel;
import org.olat.core.gui.media.MediaResource;
import org.olat.core.gui.render.EmptyURLBuilder;
import org.olat.core.gui.render.StringOutput;
import org.olat.core.gui.render.StringOutputPool;
import org.olat.core.gui.render.URLBuilder;
import org.olat.core.gui.translator.Translator;
import org.olat.core.logging.OLog;
import org.olat.core.logging.Tracing;
import org.olat.core.util.Formatter;
import org.olat.core.util.StringHelper;
import org.olat.core.util.filter.FilterFactory;
import org.olat.core.util.openxml.OpenXMLWorkbook;
import org.olat.core.util.openxml.OpenXMLWorkbookResource;
import org.olat.core.util.openxml.OpenXMLWorksheet;
import org.olat.core.util.openxml.OpenXMLWorksheet.Row;
import org.olat.course.nodes.cl.model.Checkbox;
import org.olat.course.nodes.cl.model.CheckboxList;
/**
*
* Initial date: 14.02.2014<br>
* @author srosse, stephane.rosse@frentix.com, http://www.frentix.com
*
*/
public class CheckListAssessmentDataModel extends DefaultFlexiTableDataModel<CheckListAssessmentRow>
implements FilterableFlexiTableModel, SortableFlexiTableDataModel<CheckListAssessmentRow>,
ExportableFlexiTableDataModel {
private static final OLog log = Tracing.createLoggerFor(CheckListAssessmentDataModel.class);
public static final int USER_PROPS_OFFSET = 500;
public static final int CHECKBOX_OFFSET = 5000;
private final Locale locale;
private final CheckboxList checkboxList;
private List<CheckListAssessmentRow> backupRows;
public CheckListAssessmentDataModel(CheckboxList checkboxList, List<CheckListAssessmentRow> datas,
FlexiTableColumnModel columnModel, Locale locale) {
super(datas, columnModel);
backupRows = datas;
this.locale = locale;
this.checkboxList = checkboxList;
}
/**
* @return The list of rows, not filtered
*/
public List<CheckListAssessmentRow> getBackedUpRows() {
return backupRows;
}
@Override
public DefaultFlexiTableDataModel<CheckListAssessmentRow> createCopyWithEmptyList() {
return new CheckListAssessmentDataModel(checkboxList, new ArrayList<CheckListAssessmentRow>(), getTableColumnModel(), locale);
}
@Override
public void sort(SortKey orderBy) {
CheckListAssessmentDataModelSorter sorter = new CheckListAssessmentDataModelSorter(orderBy, this, locale);
List<CheckListAssessmentRow> views = sorter.sort();
super.setObjects(views);
}
@Override
public MediaResource export(FlexiTableComponent ftC) {
List<CheckListAssessmentRow> currentRows = getObjects();
setObjects(backupRows);
FlexiTableColumnModel columnModel = getTableColumnModel();
int numOfColumns = columnModel.getColumnCount();
List<FlexiColumnModel> columns = new ArrayList<>(numOfColumns);
for(int i=0; i<numOfColumns; i++) {
FlexiColumnModel column = columnModel.getColumnModel(i);
String headerKey = column.getHeaderKey();
if(!"edit.checkbox".equals(headerKey)) {
columns.add(column);
}
}
CheckListXlsFlexiTableExporter exporter = new CheckListXlsFlexiTableExporter();
MediaResource resource = exporter.export(ftC, this, columns, ftC.getTranslator());
//replace the current perhaps filtered rows
super.setObjects(currentRows);
return resource;
}
/**
* The filter apply to the groups
* @param key
*/
@Override
public void filter(List<FlexiTableFilter> filters) {
setObjects(backupRows);
Long groupKey = extractGroupKey(filters);
if(groupKey != null) {
List<CheckListAssessmentRow> filteredViews = new ArrayList<>();
int numOfRows = getRowCount();
for(int i=0; i<numOfRows; i++) {
CheckListAssessmentRow view = getObject(i);
if(accept(view, groupKey)) {
filteredViews.add(view);
}
}
super.setObjects(filteredViews);
}
}
private Long extractGroupKey(List<FlexiTableFilter> filters) {
Long key = null;
if(filters != null && filters.size() > 0 && filters.get(0) != null) {
String filter = filters.get(0).getFilter();
if(StringHelper.isLong(filter)) {
try {
key = Long.parseLong(filter);
} catch (NumberFormatException e) {
//
}
}
}
return key;
}
private boolean accept(CheckListAssessmentRow view, Long groupKey) {
boolean accept = false;
Long[] groupKeys = view.getGroupKeys();
if(groupKeys != null) {
for(Long key:groupKeys) {
if(groupKey.equals(key)) {
accept = true;
}
}
}
return accept;
}
@Override
public void setObjects(List<CheckListAssessmentRow> objects) {
backupRows = objects;
super.setObjects(objects);
}
@Override
public Object getValueAt(int row, int col) {
CheckListAssessmentRow box = getObject(row);
return getValueAt(box, col);
}
@Override
public Object getValueAt(CheckListAssessmentRow row, int col) {
if(col == Cols.username.ordinal()) {
return row.getIdentityName();
} else if(col == Cols.totalPoints.ordinal()) {
return row.getTotalPoints();
} else if(col >= USER_PROPS_OFFSET && col < CHECKBOX_OFFSET) {
int propIndex = col - USER_PROPS_OFFSET;
return row.getIdentityProp(propIndex);
} else if(col >= CHECKBOX_OFFSET) {
int propIndex = col - CHECKBOX_OFFSET;
if(row.getCheckedEl() != null) {
//edit mode
MultipleSelectionElement[] checked = row.getCheckedEl();
if(checked != null && propIndex >= 0 && propIndex < checked.length) {
return checked[propIndex];
}
}
Boolean[] checked = row.getChecked();
if(checked != null && propIndex >= 0 && propIndex < checked.length
&& checked[propIndex] != null && checked[propIndex].booleanValue()) {
return checked[propIndex];
}
return null;
}
return row;
}
public enum Cols {
username("username"),
totalPoints("points");
private final String i18nKey;
private Cols(String i18nKey) {
this.i18nKey = i18nKey;
}
public String i18nKey() {
return i18nKey;
}
}
private static class CheckListXlsFlexiTableExporter {
private static final URLBuilder ubu = new EmptyURLBuilder();
private CheckListAssessmentDataModel dataModel;
public MediaResource export(FlexiTableComponent ftC, CheckListAssessmentDataModel model,
List<FlexiColumnModel> columns, Translator translator) {
this.dataModel = model;
String label = "CheckList_"
+ Formatter.formatDatetimeFilesystemSave(new Date(System.currentTimeMillis()))
+ ".xlsx";
return new OpenXMLWorkbookResource(label) {
@Override
protected void generate(OutputStream out) {
try(OpenXMLWorkbook workbook = new OpenXMLWorkbook(out, 1)) {
OpenXMLWorksheet sheet = workbook.nextWorksheet();
createHeader(columns, translator, sheet, workbook);
createData(ftC, columns, translator, sheet);
} catch (IOException e) {
log.error("", e);
}
}
};
}
private void createHeader(List<FlexiColumnModel> columns, Translator translator,
OpenXMLWorksheet sheet, OpenXMLWorkbook workbook) {
sheet.setHeaderRows(1);
Row headerRow = sheet.newRow();
int pos = 0;
for (int c=0; c<columns.size(); c++) {
FlexiColumnModel cd = columns.get(c);
String headerVal = cd.getHeaderLabel() == null ?
translator.translate(cd.getHeaderKey()) : cd.getHeaderLabel();
headerRow.addCell(pos++, headerVal, workbook.getStyles().getHeaderStyle());
if(cd.getColumnIndex() >= CHECKBOX_OFFSET) {
int propIndex = cd.getColumnIndex() - CHECKBOX_OFFSET;
Checkbox box = dataModel.checkboxList.getList().get(propIndex);
if(box.getPoints() != null && box.getPoints().floatValue() > 0f) {
headerRow.addCell(pos++, "", workbook.getStyles().getHeaderStyle());
}
}
}
}
private void createData(FlexiTableComponent ftC, List<FlexiColumnModel> columns, Translator translator, OpenXMLWorksheet sheet) {
int numOfRow = dataModel.getRowCount();
int numOfColumns = columns.size();
for (int r=0; r<numOfRow; r++) {
int pos = 0;
Row dataRow = sheet.newRow();
for (int c = 0; c<numOfColumns; c++) {
FlexiColumnModel cd = columns.get(c);
Object value = dataModel.getValueAt(r, cd.getColumnIndex());
if(cd.getColumnIndex() >= CHECKBOX_OFFSET) {
int propIndex = cd.getColumnIndex() - CHECKBOX_OFFSET;
Checkbox box = dataModel.checkboxList.getList().get(propIndex);
boolean checked;
if(value instanceof Boolean) {
checked = ((Boolean)value).booleanValue();
} else {
checked = false;
}
String checkVal = checked ? "x" : "";
dataRow.addCell(pos++, checkVal, null);
if(box.getPoints() != null && box.getPoints().floatValue() > 0f) {
CheckListAssessmentRow assessmentRow = dataModel.getObject(r);
Float[] scores = assessmentRow.getScores();
if(checked && scores != null && scores.length > 0 && propIndex < scores.length) {
dataRow.addCell(pos++, scores[propIndex], null);
}
}
} else {
renderCell(dataRow, pos++, value, r, ftC, cd, translator);
}
}
}
}
protected void renderCell(Row dataRow, int sheetCol, Object value, int row, FlexiTableComponent ftC, FlexiColumnModel cd, Translator translator) {
if(value instanceof Boolean) {
Boolean val = (Boolean)value;
dataRow.addCell(sheetCol, val.booleanValue() ? "x" : "", null);
} else if(value instanceof Float || value instanceof Double) {
dataRow.addCell(sheetCol, (Number)value, null);
} else {
StringOutput so = StringOutputPool.allocStringBuilder(1000);
cd.getCellRenderer().render(null, so, value, row, ftC, ubu, translator);
String cellValue = StringOutputPool.freePop(so);
cellValue = StringHelper.stripLineBreaks(cellValue);
cellValue = FilterFactory.getHtmlTagsFilter().filter(cellValue);
if(StringHelper.containsNonWhitespace(cellValue)) {
cellValue = StringEscapeUtils.unescapeHtml(cellValue);
}
dataRow.addCell(sheetCol, cellValue, null);
}
}
}
}